// +build !integration,!e2e // Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. package commands import ( "errors" "io/ioutil" "os" "testing" mock_engine "github.com/aws/amazon-ecs-cni-plugins/plugins/eni/engine/mocks" "github.com/cihub/seelog" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/version" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const ( eniID = "eni1" deviceName = "eth1" nsName = "ns1" eniIPV4Address = "10.11.12.13/20" eniIPV6Address = "2001:db8::68/64" eniIPV4Gateway = "10.10.10.10" eniIPV6Gateway = "fe80:db9::68" macAddress = "01:23:45:67:89:ab" ) var ( eniArgs = &skel.CmdArgs{ StdinData: []byte(`{"cniVersion": "0.3.0",` + `"eni":"` + eniID + `", "mac":"` + macAddress + `", "ip-addresses":["` + eniIPV4Address + `","` + eniIPV6Address + `"]` + ` , "gateway-ip-addresses":["` + eniIPV4Gateway + `","` + eniIPV6Gateway + `"]` + `}`), Netns: nsName, IfName: "eth0", } eniArgsNoIPV6 = &skel.CmdArgs{ StdinData: []byte(`{"cniVersion": "0.3.0",` + `"eni":"` + eniID + `", "mac":"` + macAddress + `", "ip-addresses":["` + eniIPV4Address + `"]` + ` , "gateway-ip-addresses":["` + eniIPV4Gateway + `"]` + `}`), Netns: nsName, IfName: "eth0", } eniArgsStayDown = &skel.CmdArgs{ StdinData: []byte(`{"cniVersion": "0.3.0",` + `"eni":"` + eniID + `", "mac":"` + macAddress + `", "ip-addresses":["` + eniIPV4Address + `","` + eniIPV6Address + `"]` + ` , "gateway-ip-addresses":["` + eniIPV4Gateway + `","` + eniIPV6Gateway + `"]` + ` , "stay-down":true` + `}`), Netns: nsName, IfName: "eth0", } ) func TestAddWithInvalidConfig(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) err := add(&skel.CmdArgs{}, mockEngine) assert.Error(t, err) } func TestAddGetInterfaceDeviceNameFails(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().GetInterfaceDeviceName(macAddress).Return("", errors.New("error")), ) err := add(eniArgs, mockEngine) assert.Error(t, err) } func TestAddSetupContainerNamespaceFails(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().GetInterfaceDeviceName(macAddress).Return(deviceName, nil), mockEngine.EXPECT().SetupContainerNamespace(gomock.Any(), deviceName, macAddress, []string{eniIPV4Address, eniIPV6Address}, []string{eniIPV4Gateway, eniIPV6Gateway}, false, false, 0).Return(errors.New("error")), ) err := add(eniArgs, mockEngine) assert.Error(t, err) } func TestAddSetupContainerNamespaceFailsNoIPV6(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().GetInterfaceDeviceName(macAddress).Return(deviceName, nil), mockEngine.EXPECT().SetupContainerNamespace(gomock.Any(), deviceName, macAddress, []string{eniIPV4Address}, []string{eniIPV4Gateway}, false, false, 0).Return(errors.New("error")), ) err := add(eniArgsNoIPV6, mockEngine) assert.Error(t, err) } func TestAddNoError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().GetInterfaceDeviceName(macAddress).Return(deviceName, nil), mockEngine.EXPECT().SetupContainerNamespace(gomock.Any(), deviceName, macAddress, []string{eniIPV4Address, eniIPV6Address}, []string{eniIPV4Gateway, eniIPV6Gateway}, false, false, 0).Return(nil), ) err := add(eniArgs, mockEngine) assert.NoError(t, err) } func TestAddStayDownNoError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().GetInterfaceDeviceName(macAddress).Return(deviceName, nil), mockEngine.EXPECT().SetupContainerNamespace(gomock.Any(), deviceName, macAddress, []string{eniIPV4Address, eniIPV6Address}, []string{eniIPV4Gateway, eniIPV6Gateway}, false, true, 0).Return(nil), ) err := add(eniArgsStayDown, mockEngine) assert.NoError(t, err) } func TestAddNoErrorWhenIPV6AddressNotSpecifiedInConfig(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().GetInterfaceDeviceName(macAddress).Return(deviceName, nil), mockEngine.EXPECT().SetupContainerNamespace(gomock.Any(), deviceName, macAddress, []string{eniIPV4Address}, []string{eniIPV4Gateway}, false, false, 0).Return(nil), ) err := add(eniArgsNoIPV6, mockEngine) assert.NoError(t, err) } // TestAddPrintResult tests the add command return compatiable result as cni func TestAddPrintResult(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Turn off the log for test, as the test needs to read the result returned by the plugin from stdout logger, err := seelog.LoggerFromConfigAsString(` `) assert.NoError(t, err, "create new logger failed") err = seelog.ReplaceLogger(logger) assert.NoError(t, err, "turn off the logger failed") mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().GetInterfaceDeviceName(macAddress).Return(deviceName, nil), mockEngine.EXPECT().SetupContainerNamespace(gomock.Any(), deviceName, macAddress, []string{eniIPV4Address, eniIPV6Address}, []string{eniIPV4Gateway, eniIPV6Gateway}, false, false, 0).Return(nil), ) oldStdout := os.Stdout r, w, err := os.Pipe() require.NoError(t, err, "redirect os.stdin succeed") os.Stdout = w err = add(eniArgs, mockEngine) assert.NoError(t, err) w.Close() output, err := ioutil.ReadAll(r) require.NoError(t, err, "read from stdin failed") os.Stdout = oldStdout res, err := version.NewResult("0.3.0", output) require.NoError(t, err, "construct result from stdin failed: %s", string(output)) result, err := current.GetResult(res) assert.NoError(t, err, "convert result to current version failed") assert.Equal(t, deviceName, result.Interfaces[0].Name) assert.Equal(t, macAddress, result.Interfaces[0].Mac) assert.Equal(t, 2, len(result.IPs), "result should contains information of both ipv4 and ipv6") } func TestAddPrintResultNoIPV6(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() // Turn off the log for test, as the test needs to read the result returned by the plugin from stdout logger, err := seelog.LoggerFromConfigAsString(` `) assert.NoError(t, err, "create new logger failed") err = seelog.ReplaceLogger(logger) assert.NoError(t, err, "turn off the logger failed") mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().GetInterfaceDeviceName(macAddress).Return(deviceName, nil), mockEngine.EXPECT().SetupContainerNamespace(gomock.Any(), deviceName, macAddress, []string{eniIPV4Address}, []string{eniIPV4Gateway}, false, false, 0).Return(nil), ) oldStdout := os.Stdout r, w, err := os.Pipe() require.NoError(t, err, "redirect os.stdin succeed") os.Stdout = w err = add(eniArgsNoIPV6, mockEngine) assert.NoError(t, err) w.Close() output, err := ioutil.ReadAll(r) require.NoError(t, err, "read from stdin failed") os.Stdout = oldStdout res, err := version.NewResult("0.3.0", output) require.NoError(t, err, "construct result from stdin failed") result, err := current.GetResult(res) assert.NoError(t, err, "convert result to current version failed") assert.Equal(t, deviceName, result.Interfaces[0].Name) assert.Equal(t, macAddress, result.Interfaces[0].Mac) assert.Equal(t, 1, len(result.IPs), "result should only contains information of ipv4") }