github.com/k8snetworkplumbingwg/sriov-network-operator@v1.2.1-0.20240408194816-2d2e5a45d453/pkg/plugins/k8s/k8s_plugin_test.go (about) 1 package k8s 2 3 import ( 4 "fmt" 5 "os" 6 "testing" 7 8 "github.com/golang/mock/gomock" 9 . "github.com/onsi/ginkgo/v2" 10 . "github.com/onsi/gomega" 11 "go.uber.org/zap/zapcore" 12 "sigs.k8s.io/controller-runtime/pkg/log" 13 "sigs.k8s.io/controller-runtime/pkg/log/zap" 14 15 sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1" 16 mock_helper "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/helper/mock" 17 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host" 18 hostTypes "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types" 19 plugin "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/plugins" 20 "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars" 21 ) 22 23 func TestK8sPlugin(t *testing.T) { 24 log.SetLogger(zap.New( 25 zap.WriteTo(GinkgoWriter), 26 zap.Level(zapcore.Level(-2)), 27 zap.UseDevMode(true))) 28 RegisterFailHandler(Fail) 29 RunSpecs(t, "Test K8s Plugin") 30 } 31 32 // changes current working dir before calling the real function 33 func registerCall(m *gomock.Call, realF interface{}) *gomock.Call { 34 cur, _ := os.Getwd() 35 return m.Do(func(_ ...interface{}) { 36 os.Chdir("../../..") 37 }).DoAndReturn(realF).Do(func(_ ...interface{}) { 38 os.Chdir(cur) 39 }) 40 } 41 42 func setIsSystemdMode(val bool) { 43 origUsingSystemdMode := vars.UsingSystemdMode 44 DeferCleanup(func() { 45 vars.UsingSystemdMode = origUsingSystemdMode 46 }) 47 vars.UsingSystemdMode = val 48 } 49 50 func newServiceNameMatcher(name string) gomock.Matcher { 51 return &serviceNameMatcher{name: name} 52 } 53 54 type serviceNameMatcher struct { 55 name string 56 } 57 58 func (snm *serviceNameMatcher) Matches(x interface{}) bool { 59 s, ok := x.(*hostTypes.Service) 60 if !ok { 61 return false 62 } 63 return snm.name == s.Name 64 } 65 66 func (snm *serviceNameMatcher) String() string { 67 return "service name match: " + snm.name 68 } 69 70 var _ = Describe("K8s plugin", func() { 71 var ( 72 k8sPlugin plugin.VendorPlugin 73 err error 74 testCtrl *gomock.Controller 75 hostHelper *mock_helper.MockHostHelpersInterface 76 ) 77 78 BeforeEach(func() { 79 testCtrl = gomock.NewController(GinkgoT()) 80 81 hostHelper = mock_helper.NewMockHostHelpersInterface(testCtrl) 82 realHostMgr := host.NewHostManager(hostHelper) 83 84 // proxy some functions to real host manager to simplify testing and to additionally validate manifests 85 for _, f := range []string{ 86 "bindata/manifests/sriov-config-service/kubernetes/sriov-config-service.yaml", 87 "bindata/manifests/sriov-config-service/kubernetes/sriov-config-post-network-service.yaml", 88 } { 89 registerCall(hostHelper.EXPECT().ReadServiceManifestFile(f), realHostMgr.ReadServiceManifestFile) 90 } 91 for _, s := range []string{ 92 "bindata/manifests/switchdev-config/ovs-units/ovs-vswitchd.service.yaml", 93 } { 94 registerCall(hostHelper.EXPECT().ReadServiceInjectionManifestFile(s), realHostMgr.ReadServiceInjectionManifestFile) 95 } 96 k8sPlugin, err = NewK8sPlugin(hostHelper) 97 Expect(err).ToNot(HaveOccurred()) 98 }) 99 100 AfterEach(func() { 101 testCtrl.Finish() 102 }) 103 104 It("no switchdev, no systemd", func() { 105 setIsSystemdMode(false) 106 needDrain, needReboot, err := k8sPlugin.OnNodeStateChange(&sriovnetworkv1.SriovNetworkNodeState{}) 107 Expect(err).ToNot(HaveOccurred()) 108 Expect(needReboot).To(BeFalse()) 109 Expect(needDrain).To(BeFalse()) 110 Expect(k8sPlugin.Apply()).NotTo(HaveOccurred()) 111 }) 112 113 It("systemd, created", func() { 114 setIsSystemdMode(true) 115 116 hostHelper.EXPECT().IsServiceEnabled("/etc/systemd/system/sriov-config.service").Return(false, nil) 117 hostHelper.EXPECT().IsServiceEnabled("/etc/systemd/system/sriov-config-post-network.service").Return(false, nil) 118 hostHelper.EXPECT().EnableService(newServiceNameMatcher("sriov-config.service")).Return(nil) 119 hostHelper.EXPECT().EnableService(newServiceNameMatcher("sriov-config-post-network.service")).Return(nil) 120 121 needDrain, needReboot, err := k8sPlugin.OnNodeStateChange(&sriovnetworkv1.SriovNetworkNodeState{}) 122 Expect(err).ToNot(HaveOccurred()) 123 Expect(needReboot).To(BeTrue()) 124 Expect(needDrain).To(BeTrue()) 125 Expect(k8sPlugin.Apply()).NotTo(HaveOccurred()) 126 }) 127 It("systemd, already configured", func() { 128 setIsSystemdMode(true) 129 130 hostHelper.EXPECT().IsServiceEnabled("/etc/systemd/system/sriov-config.service").Return(true, nil) 131 hostHelper.EXPECT().ReadService("/etc/systemd/system/sriov-config.service").Return( 132 &hostTypes.Service{Name: "sriov-config.service"}, nil) 133 hostHelper.EXPECT().CompareServices( 134 &hostTypes.Service{Name: "sriov-config.service"}, 135 newServiceNameMatcher("sriov-config.service"), 136 ).Return(false, nil) 137 138 hostHelper.EXPECT().IsServiceEnabled("/etc/systemd/system/sriov-config-post-network.service").Return(true, nil) 139 hostHelper.EXPECT().ReadService("/etc/systemd/system/sriov-config-post-network.service").Return( 140 &hostTypes.Service{Name: "sriov-config-post-network.service"}, nil) 141 hostHelper.EXPECT().CompareServices(&hostTypes.Service{Name: "sriov-config-post-network.service"}, 142 newServiceNameMatcher("sriov-config-post-network.service"), 143 ).Return(false, nil) 144 145 needDrain, needReboot, err := k8sPlugin.OnNodeStateChange(&sriovnetworkv1.SriovNetworkNodeState{}) 146 Expect(err).ToNot(HaveOccurred()) 147 Expect(needReboot).To(BeFalse()) 148 Expect(needDrain).To(BeFalse()) 149 Expect(k8sPlugin.Apply()).NotTo(HaveOccurred()) 150 }) 151 It("systemd, update required", func() { 152 setIsSystemdMode(true) 153 154 hostHelper.EXPECT().IsServiceEnabled("/etc/systemd/system/sriov-config.service").Return(true, nil) 155 hostHelper.EXPECT().ReadService("/etc/systemd/system/sriov-config.service").Return( 156 &hostTypes.Service{Name: "sriov-config.service"}, nil) 157 hostHelper.EXPECT().CompareServices( 158 &hostTypes.Service{Name: "sriov-config.service"}, 159 newServiceNameMatcher("sriov-config.service"), 160 ).Return(true, nil) 161 hostHelper.EXPECT().EnableService(newServiceNameMatcher("sriov-config.service")).Return(nil) 162 163 hostHelper.EXPECT().IsServiceEnabled("/etc/systemd/system/sriov-config-post-network.service").Return(true, nil) 164 hostHelper.EXPECT().ReadService("/etc/systemd/system/sriov-config-post-network.service").Return( 165 &hostTypes.Service{Name: "sriov-config-post-network.service"}, nil) 166 hostHelper.EXPECT().CompareServices(&hostTypes.Service{Name: "sriov-config-post-network.service"}, 167 newServiceNameMatcher("sriov-config-post-network.service"), 168 ).Return(false, nil) 169 170 needDrain, needReboot, err := k8sPlugin.OnNodeStateChange(&sriovnetworkv1.SriovNetworkNodeState{}) 171 Expect(err).ToNot(HaveOccurred()) 172 Expect(needReboot).To(BeTrue()) 173 Expect(needDrain).To(BeTrue()) 174 Expect(k8sPlugin.Apply()).NotTo(HaveOccurred()) 175 }) 176 It("ovs service not found", func() { 177 setIsSystemdMode(false) 178 hostHelper.EXPECT().IsServiceExist("/usr/lib/systemd/system/ovs-vswitchd.service").Return(false, nil) 179 needDrain, needReboot, err := k8sPlugin.OnNodeStateChange(&sriovnetworkv1.SriovNetworkNodeState{ 180 Spec: sriovnetworkv1.SriovNetworkNodeStateSpec{Interfaces: []sriovnetworkv1.Interface{{EswitchMode: "switchdev"}}}}) 181 Expect(err).ToNot(HaveOccurred()) 182 Expect(needReboot).To(BeFalse()) 183 Expect(needDrain).To(BeFalse()) 184 Expect(k8sPlugin.Apply()).NotTo(HaveOccurred()) 185 }) 186 It("ovs service updated", func() { 187 setIsSystemdMode(false) 188 hostHelper.EXPECT().IsServiceExist("/usr/lib/systemd/system/ovs-vswitchd.service").Return(true, nil) 189 hostHelper.EXPECT().ReadService("/usr/lib/systemd/system/ovs-vswitchd.service").Return( 190 &hostTypes.Service{Name: "ovs-vswitchd.service"}, nil) 191 hostHelper.EXPECT().CompareServices( 192 &hostTypes.Service{Name: "ovs-vswitchd.service"}, 193 newServiceNameMatcher("ovs-vswitchd.service"), 194 ).Return(true, nil) 195 hostHelper.EXPECT().Chroot("/host").Return(nil, fmt.Errorf("test")) 196 hostHelper.EXPECT().UpdateSystemService(newServiceNameMatcher("ovs-vswitchd.service")).Return(nil) 197 needDrain, needReboot, err := k8sPlugin.OnNodeStateChange(&sriovnetworkv1.SriovNetworkNodeState{ 198 Spec: sriovnetworkv1.SriovNetworkNodeStateSpec{Interfaces: []sriovnetworkv1.Interface{{EswitchMode: "switchdev"}}}}) 199 Expect(err).ToNot(HaveOccurred()) 200 Expect(needReboot).To(BeTrue()) 201 Expect(needDrain).To(BeTrue()) 202 Expect(k8sPlugin.Apply()).NotTo(HaveOccurred()) 203 }) 204 It("ovs service updated - hw offloading already enabled", func() { 205 setIsSystemdMode(false) 206 hostHelper.EXPECT().IsServiceExist("/usr/lib/systemd/system/ovs-vswitchd.service").Return(true, nil) 207 hostHelper.EXPECT().ReadService("/usr/lib/systemd/system/ovs-vswitchd.service").Return( 208 &hostTypes.Service{Name: "ovs-vswitchd.service"}, nil) 209 hostHelper.EXPECT().CompareServices( 210 &hostTypes.Service{Name: "ovs-vswitchd.service"}, 211 newServiceNameMatcher("ovs-vswitchd.service"), 212 ).Return(true, nil) 213 hostHelper.EXPECT().Chroot("/host").Return(func() error { return nil }, nil) 214 hostHelper.EXPECT().RunCommand("ovs-vsctl", "get", "Open_vSwitch", ".", "other_config:hw-offload").Return("\"true\"\n", "", nil) 215 hostHelper.EXPECT().UpdateSystemService(newServiceNameMatcher("ovs-vswitchd.service")).Return(nil) 216 needDrain, needReboot, err := k8sPlugin.OnNodeStateChange(&sriovnetworkv1.SriovNetworkNodeState{ 217 Spec: sriovnetworkv1.SriovNetworkNodeStateSpec{Interfaces: []sriovnetworkv1.Interface{{EswitchMode: "switchdev"}}}}) 218 Expect(err).ToNot(HaveOccurred()) 219 Expect(needReboot).To(BeFalse()) 220 Expect(needDrain).To(BeFalse()) 221 Expect(k8sPlugin.Apply()).NotTo(HaveOccurred()) 222 }) 223 })