// +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" "net" "testing" "github.com/aws/amazon-ecs-cni-plugins/plugins/ecs-bridge/engine/mocks" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types/current" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/vishvananda/netlink" ) const ( bridgeName = "ecs-br0" defaultMTU = 1500 nsName = "ecs-eni" interfaceName = "ecs-veth0" hostVethName = "ecs-host-veth0" ipamType = "ecs-ipam" mac = "01:23:45:67:89:ab" ) var conf = &skel.CmdArgs{ StdinData: []byte(`{"bridge":"` + bridgeName + `", "ipam":{"type": "` + ipamType + `"}}`), Netns: nsName, IfName: interfaceName, } var emptyConf = &skel.CmdArgs{ StdinData: []byte(""), Netns: nsName, } var macHWAddr net.HardwareAddr func init() { macHWAddr, _ = net.ParseMAC(mac) } // TODO: Add integration tests for command.Add commands.Del func TestAddConfError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) err := add(emptyConf, mockEngine) assert.Error(t, err) } func TestAddCreateBridgeError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) mockEngine.EXPECT().CreateBridge(bridgeName, defaultMTU).Return(nil, errors.New("error")) err := add(conf, mockEngine) assert.Error(t, err) } func TestAddCreateVethPairError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().CreateBridge(bridgeName, defaultMTU).Return(nil, nil), mockEngine.EXPECT().CreateVethPair(nsName, defaultMTU, interfaceName).Return(nil, "", errors.New("error")), ) err := add(conf, mockEngine) assert.Error(t, err) } func TestAddAttachHostVethInterfaceToBridgeError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) bridgeLink := &netlink.Bridge{} gomock.InOrder( mockEngine.EXPECT().CreateBridge(bridgeName, defaultMTU).Return(bridgeLink, nil), mockEngine.EXPECT().CreateVethPair(nsName, defaultMTU, interfaceName).Return(nil, hostVethName, nil), mockEngine.EXPECT().AttachHostVethInterfaceToBridge(hostVethName, bridgeLink).Return(nil, errors.New("error")), ) err := add(conf, mockEngine) assert.Error(t, err) } func TestAddRunIPAMPlginAddError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) bridgeLink := &netlink.Bridge{} gomock.InOrder( mockEngine.EXPECT().CreateBridge(bridgeName, defaultMTU).Return(bridgeLink, nil), mockEngine.EXPECT().CreateVethPair(nsName, defaultMTU, interfaceName).Return(nil, hostVethName, nil), mockEngine.EXPECT().AttachHostVethInterfaceToBridge(hostVethName, bridgeLink).Return(nil, nil), mockEngine.EXPECT().RunIPAMPluginAdd(ipamType, conf.StdinData).Return(nil, errors.New("error")), ) err := add(conf, mockEngine) assert.Error(t, err) } func TestAddConfigureContainerVethInterfaceError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) bridgeLink := &netlink.Bridge{ LinkAttrs: netlink.LinkAttrs{ Name: bridgeName, HardwareAddr: macHWAddr, }, } result := ¤t.Result{ IPs: []*current.IPConfig{ ¤t.IPConfig{}, }, } containerVethInterface := ¤t.Interface{} hostVethInterface := ¤t.Interface{} gomock.InOrder( mockEngine.EXPECT().CreateBridge(bridgeName, defaultMTU).Return(bridgeLink, nil), mockEngine.EXPECT().CreateVethPair(nsName, defaultMTU, interfaceName).Return(containerVethInterface, hostVethName, nil), mockEngine.EXPECT().AttachHostVethInterfaceToBridge(hostVethName, bridgeLink).Return(hostVethInterface, nil), mockEngine.EXPECT().RunIPAMPluginAdd(ipamType, conf.StdinData).Return(result, nil), mockEngine.EXPECT().ConfigureContainerVethInterface(nsName, result, interfaceName).Do( func(netns string, res *current.Result, ifName string) { assert.NotEmpty(t, res) assert.Equal(t, 3, len(res.Interfaces)) assert.Equal(t, 2, res.IPs[0].Interface) bridge := res.Interfaces[0] assert.Equal(t, bridgeName, bridge.Name) assert.Equal(t, mac, bridge.Mac) }).Return(errors.New("error")), ) err := add(conf, mockEngine) assert.Error(t, err) } func TestAddConfigureBridgeError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) bridgeLink := &netlink.Bridge{ LinkAttrs: netlink.LinkAttrs{ Name: bridgeName, HardwareAddr: macHWAddr, }, } result := ¤t.Result{ IPs: []*current.IPConfig{ ¤t.IPConfig{}, }, } containerVethInterface := ¤t.Interface{} hostVethInterface := ¤t.Interface{} gomock.InOrder( mockEngine.EXPECT().CreateBridge(bridgeName, defaultMTU).Return(bridgeLink, nil), mockEngine.EXPECT().CreateVethPair(nsName, defaultMTU, interfaceName).Return(containerVethInterface, hostVethName, nil), mockEngine.EXPECT().AttachHostVethInterfaceToBridge(hostVethName, bridgeLink).Return(hostVethInterface, nil), mockEngine.EXPECT().RunIPAMPluginAdd(ipamType, conf.StdinData).Return(result, nil), mockEngine.EXPECT().ConfigureContainerVethInterface(nsName, result, interfaceName).Return(nil), mockEngine.EXPECT().ConfigureBridge(result, bridgeLink).Return(errors.New("error")), ) err := add(conf, mockEngine) assert.Error(t, err) } func TestAddSuccess(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) bridgeLink := &netlink.Bridge{ LinkAttrs: netlink.LinkAttrs{ Name: bridgeName, HardwareAddr: macHWAddr, }, } result := ¤t.Result{ IPs: []*current.IPConfig{ ¤t.IPConfig{}, }, } containerVethInterface := ¤t.Interface{} hostVethInterface := ¤t.Interface{} gomock.InOrder( mockEngine.EXPECT().CreateBridge(bridgeName, defaultMTU).Return(bridgeLink, nil), mockEngine.EXPECT().CreateVethPair(nsName, defaultMTU, interfaceName).Return(containerVethInterface, hostVethName, nil), mockEngine.EXPECT().AttachHostVethInterfaceToBridge(hostVethName, bridgeLink).Return(hostVethInterface, nil), mockEngine.EXPECT().RunIPAMPluginAdd(ipamType, conf.StdinData).Return(result, nil), mockEngine.EXPECT().ConfigureContainerVethInterface(nsName, result, interfaceName).Do( func(netns string, res *current.Result, ifName string) { assert.NotEmpty(t, res) assert.Equal(t, 3, len(res.Interfaces)) assert.Equal(t, 2, res.IPs[0].Interface) bridge := res.Interfaces[0] assert.Equal(t, bridgeName, bridge.Name) assert.Equal(t, mac, bridge.Mac) }).Return(nil), mockEngine.EXPECT().ConfigureBridge(result, bridgeLink).Return(nil), ) err := add(conf, mockEngine) assert.NoError(t, err) } func TestDelNewConfError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) err := del(emptyConf, mockEngine) assert.Error(t, err) } func TestDelRunIPAMPluginDelError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) mockEngine.EXPECT().RunIPAMPluginDel(ipamType, conf.StdinData).Return(errors.New("error")) mockEngine.EXPECT().DeleteVeth(nsName, interfaceName).Return(nil) err := del(conf, mockEngine) // the final error that gets returned is from DeleteVeth - we log the error for IPAM delete and then carry on assert.NoError(t, err) } func TestDelDeleteVethError(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().RunIPAMPluginDel(ipamType, conf.StdinData).Return(nil), mockEngine.EXPECT().DeleteVeth(nsName, interfaceName).Return(errors.New("error")), ) err := del(conf, mockEngine) assert.Error(t, err) } func TestDelSuccess(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() mockEngine := mock_engine.NewMockEngine(ctrl) gomock.InOrder( mockEngine.EXPECT().RunIPAMPluginDel(ipamType, conf.StdinData).Return(nil), mockEngine.EXPECT().DeleteVeth(nsName, interfaceName).Return(nil), ) err := del(conf, mockEngine) assert.NoError(t, err) }