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  })