From 15f75c0080dc9b9338722a3473c0529519404e08 Mon Sep 17 00:00:00 2001 From: Balazs Nadasdi Date: Thu, 21 Oct 2021 18:08:51 +0200 Subject: [PATCH] Add steps/network tests Related to #155 --- core/steps/network/interface_create.go | 4 + core/steps/network/interface_create_test.go | 311 ++++++++++++++++++++ core/steps/network/interface_delete_test.go | 154 ++++++++++ 3 files changed, 469 insertions(+) create mode 100644 core/steps/network/interface_create_test.go create mode 100644 core/steps/network/interface_delete_test.go diff --git a/core/steps/network/interface_create.go b/core/steps/network/interface_create.go index 4fd3cceb..96b55a16 100644 --- a/core/steps/network/interface_create.go +++ b/core/steps/network/interface_create.go @@ -78,6 +78,10 @@ func (s *createInterface) Do(ctx context.Context) ([]planner.Procedure, error) { return nil, fmt.Errorf("checking if networking interface exists: %w", err) } if exists { + // This whole block is unreachable right now, because + // the Do function is called only if ShouldDo returns + // true. It retruns false if IfaceExists returns true. + // Line 76 will never return exists=true details, err := s.svc.IfaceDetails(ctx, deviceName) if err != nil { return nil, fmt.Errorf("getting interface details: %w", err) diff --git a/core/steps/network/interface_create_test.go b/core/steps/network/interface_create_test.go new file mode 100644 index 00000000..53501023 --- /dev/null +++ b/core/steps/network/interface_create_test.go @@ -0,0 +1,311 @@ +package network_test + +import ( + "context" + "testing" + + "github.com/golang/mock/gomock" + g "github.com/onsi/gomega" + "github.com/weaveworks/flintlock/core/errors" + "github.com/weaveworks/flintlock/core/models" + "github.com/weaveworks/flintlock/core/ports" + "github.com/weaveworks/flintlock/core/steps/network" + "github.com/weaveworks/flintlock/infrastructure/mock" +) + +func TestNewNetworkInterface_everythingIsEmpty(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + var status *models.NetworkInterfaceStatus + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{} + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Times(0) + + step := network.NewNetworkInterface(vmid, iface, status, svc) + shouldDo, err := step.ShouldDo(ctx) + + g.Expect(err).To(g.BeNil()) + g.Expect(shouldDo).To(g.BeTrue()) + + _, err = step.Do(ctx) + + g.Expect(err).To(g.MatchError(errors.ErrGuestDeviceNameRequired)) +} + +func TestNewNetworkInterface_doesNotExist(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + var status *models.NetworkInterfaceStatus + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{GuestDeviceName: "eth0"} + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Times(0) + + step := network.NewNetworkInterface(vmid, iface, status, svc) + shouldDo, err := step.ShouldDo(ctx) + + g.Expect(err).To(g.BeNil()) + g.Expect(shouldDo).To(g.BeTrue()) + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(false, nil). + Times(1) + + svc.EXPECT(). + IfaceCreate(gomock.Eq(ctx), gomock.Eq(ports.IfaceCreateInput{ + DeviceName: "testns_testvm_tap", + })). + Return(&ports.IfaceDetails{ + DeviceName: "testns_testvm_tap", + Type: models.IfaceTypeTap, + MAC: "AA:BB:CC:DD:EE:FF", + Index: 0, + }, nil). + Times(1) + + _, err = step.Do(ctx) + + g.Expect(err).To(g.BeNil()) +} + +func TestNewNetworkInterface_existingInterface(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{ + GuestDeviceName: "eth0", + AllowMetadataRequests: false, + GuestMAC: "AA:BB:CC:DD:EE:FF", + Type: models.IfaceTypeTap, + } + status := &models.NetworkInterfaceStatus{ + HostDeviceName: "testns_testvm_tap", + Index: 0, + MACAddress: "AA:BB:CC:DD:EE:FF", + } + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(true, nil). + Times(1) + + step := network.NewNetworkInterface(vmid, iface, status, svc) + shouldDo, err := step.ShouldDo(ctx) + + g.Expect(err).To(g.BeNil()) + g.Expect(shouldDo).To(g.BeFalse()) + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(true, nil). + Times(1) + + svc.EXPECT(). + IfaceDetails(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(&ports.IfaceDetails{ + DeviceName: "testns_testvm_tap", + Type: models.IfaceTypeTap, + MAC: "AA:BB:CC:DD:EE:FF", + Index: 0, + }, nil). + Times(1) + + _, err = step.Do(ctx) + + g.Expect(err).To(g.BeNil()) +} + +func TestNewNetworkInterface_missingInterface(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{ + GuestDeviceName: "eth0", + AllowMetadataRequests: true, + GuestMAC: "AA:BB:CC:DD:EE:FF", + } + status := &models.NetworkInterfaceStatus{ + HostDeviceName: "testns_testvm_tap", + Index: 0, + MACAddress: "AA:BB:CC:DD:EE:FF", + } + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(false, nil). + Times(1) + + step := network.NewNetworkInterface(vmid, iface, status, svc) + shouldDo, err := step.ShouldDo(ctx) + + g.Expect(err).To(g.BeNil()) + g.Expect(shouldDo).To(g.BeTrue()) + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(false, nil). + Times(1) + + svc.EXPECT(). + IfaceCreate(gomock.Eq(ctx), gomock.Eq(ports.IfaceCreateInput{ + DeviceName: "testns_testvm_tap", + MAC: "AA:BB:CC:DD:EE:FF", + })). + Return(&ports.IfaceDetails{ + DeviceName: "testns_testvm_tap", + Type: models.IfaceTypeTap, + MAC: "FF:EE:DD:CC:BB:AA", + Index: 0, + }, nil). + Times(1) + + _, err = step.Do(ctx) + + g.Expect(err).To(g.BeNil()) + g.Expect(status.MACAddress).To(g.Equal("FF:EE:DD:CC:BB:AA")) +} + +func TestNewNetworkInterface_svcError(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{ + GuestDeviceName: "eth0", + AllowMetadataRequests: true, + GuestMAC: "AA:BB:CC:DD:EE:FF", + } + status := &models.NetworkInterfaceStatus{ + HostDeviceName: "testns_testvm_tap", + Index: 0, + MACAddress: "AA:BB:CC:DD:EE:FF", + } + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(false, errors.ErrParentIfaceRequired). + Times(2) + + step := network.NewNetworkInterface(vmid, iface, status, svc) + shouldDo, err := step.ShouldDo(ctx) + + g.Expect(err).ToNot(g.BeNil()) + g.Expect(shouldDo).To(g.BeFalse()) + + _, err = step.Do(ctx) + + g.Expect(err).To(g.MatchError(errors.ErrParentIfaceRequired)) +} + +func TestNewNetworkInterface_fillChangedStatus(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{ + GuestDeviceName: "eth0", + AllowMetadataRequests: true, + GuestMAC: "AA:BB:CC:DD:EE:FF", + Type: models.IfaceTypeMacvtap, + } + status := &models.NetworkInterfaceStatus{ + HostDeviceName: "testns_testvm_tap", + Index: 0, + MACAddress: "AA:BB:CC:DD:EE:FF", + } + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + step := network.NewNetworkInterface(vmid, iface, status, svc) + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_vtap")). + Return(true, nil). + Times(1) + + svc.EXPECT(). + IfaceDetails(gomock.Eq(ctx), gomock.Eq("testns_testvm_vtap")). + Return(&ports.IfaceDetails{ + DeviceName: "testns_testvm_vtap", + Type: models.IfaceTypeTap, + MAC: "FF:EE:DD:CC:BB:AA", + Index: 0, + }, nil). + Times(1) + + _, err := step.Do(ctx) + + g.Expect(err).To(g.BeNil()) + g.Expect(status.MACAddress).To(g.Equal("FF:EE:DD:CC:BB:AA")) +} + +func TestNewNetworkInterface_createError(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + var status *models.NetworkInterfaceStatus + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{GuestDeviceName: "eth0"} + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Times(0) + + step := network.NewNetworkInterface(vmid, iface, status, svc) + shouldDo, err := step.ShouldDo(ctx) + + g.Expect(err).To(g.BeNil()) + g.Expect(shouldDo).To(g.BeTrue()) + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(false, nil). + Times(1) + + svc.EXPECT(). + IfaceCreate(gomock.Eq(ctx), gomock.Eq(ports.IfaceCreateInput{ + DeviceName: "testns_testvm_tap", + })). + Return(nil, errors.ErrParentIfaceRequired). + Times(1) + + _, err = step.Do(ctx) + + g.Expect(err).To(g.MatchError(errors.ErrParentIfaceRequired)) +} diff --git a/core/steps/network/interface_delete_test.go b/core/steps/network/interface_delete_test.go new file mode 100644 index 00000000..286f45b2 --- /dev/null +++ b/core/steps/network/interface_delete_test.go @@ -0,0 +1,154 @@ +package network_test + +import ( + "context" + "testing" + + "github.com/golang/mock/gomock" + g "github.com/onsi/gomega" + "github.com/vishvananda/netlink" + "github.com/weaveworks/flintlock/core/errors" + "github.com/weaveworks/flintlock/core/models" + "github.com/weaveworks/flintlock/core/ports" + "github.com/weaveworks/flintlock/core/steps/network" + "github.com/weaveworks/flintlock/infrastructure/mock" +) + +func TestDeleteNetworkInterface_doesNotExist(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{} + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(false, nil). + Times(1) + + step := network.DeleteNetworkInterface(vmid, iface, svc) + shouldDo, err := step.ShouldDo(ctx) + + g.Expect(err).To(g.BeNil()) + g.Expect(shouldDo).To(g.BeFalse()) + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(false, nil). + Times(1) + + _, err = step.Do(ctx) + + g.Expect(err).To(g.BeNil()) +} + +func TestDeleteNetworkInterface_exists(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{} + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(true, nil). + Times(1) + + step := network.DeleteNetworkInterface(vmid, iface, svc) + shouldDo, err := step.ShouldDo(ctx) + + g.Expect(err).To(g.BeNil()) + g.Expect(shouldDo).To(g.BeTrue()) + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(true, nil). + Times(1) + + svc.EXPECT(). + IfaceDelete( + gomock.Eq(ctx), + gomock.Eq(ports.DeleteIfaceInput{DeviceName: "testns_testvm_tap"}), + ). + Return(nil). + Times(1) + + _, err = step.Do(ctx) + + g.Expect(err).To(g.BeNil()) +} + +func TestDeleteNetworkInterface_exists_errorDeleting(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{} + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(true, nil). + Times(1) + + step := network.DeleteNetworkInterface(vmid, iface, svc) + shouldDo, err := step.ShouldDo(ctx) + + g.Expect(err).To(g.BeNil()) + g.Expect(shouldDo).To(g.BeTrue()) + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(true, nil). + Times(1) + + svc.EXPECT(). + IfaceDelete( + gomock.Eq(ctx), + gomock.Eq(ports.DeleteIfaceInput{DeviceName: "testns_testvm_tap"}), + ). + Return(netlink.LinkNotFoundError{}). + Times(1) + + _, err = step.Do(ctx) + + g.Expect(err).ToNot(g.BeNil()) +} + +func TestDeleteNetworkInterface_IfaceExistsError(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + + g.RegisterTestingT(t) + + vmid, _ := models.NewVMID("testvm", "testns") + iface := &models.NetworkInterface{} + svc := mock.NewMockNetworkService(mockCtrl) + ctx := context.Background() + + svc.EXPECT(). + IfaceExists(gomock.Eq(ctx), gomock.Eq("testns_testvm_tap")). + Return(false, errors.ErrParentIfaceRequired). + Times(2) + + step := network.DeleteNetworkInterface(vmid, iface, svc) + shouldDo, err := step.ShouldDo(ctx) + + g.Expect(err).ToNot(g.BeNil()) + g.Expect(shouldDo).To(g.BeFalse()) + + _, err = step.Do(ctx) + + g.Expect(err).To(g.MatchError(errors.ErrParentIfaceRequired)) +}