...

Source file src/edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/networking/netplan/netplan_test.go

Documentation: edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/networking/netplan

     1  package netplan
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"net"
     9  	"os"
    10  	"path/filepath"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/spf13/afero"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/vishvananda/netns"
    17  	yaml "gopkg.in/yaml.v3"
    18  	v1 "k8s.io/api/core/v1"
    19  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    20  	kruntime "k8s.io/apimachinery/pkg/runtime"
    21  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    22  	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
    23  	"sigs.k8s.io/controller-runtime/pkg/client"
    24  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    25  
    26  	edgeconst "edge-infra.dev/pkg/edge/constants"
    27  	calico "edge-infra.dev/pkg/k8s/net/calico"
    28  	testnetns "edge-infra.dev/pkg/lib/kernel/netlink/netns"
    29  	"edge-infra.dev/pkg/lib/uuid"
    30  	v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
    31  	"edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
    32  	"edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/networking/netplan/internal"
    33  	nodemeta "edge-infra.dev/pkg/sds/ien/node"
    34  	"edge-infra.dev/pkg/sds/ien/topology"
    35  	"edge-infra.dev/pkg/sds/lib/networking"
    36  	"edge-infra.dev/pkg/sds/lib/networking/routing"
    37  	"edge-infra.dev/test/f2"
    38  )
    39  
    40  var (
    41  	nodeIP   = "192.168.1.2"
    42  	nodeCIDR = "192.168.1.2/24"
    43  	gateway  = "192.168.1.1"
    44  	dns      = "8.8.8.8"
    45  	iface    = "ens4"
    46  )
    47  
    48  type netplanMock struct {
    49  	err error
    50  }
    51  
    52  func (nm *netplanMock) Apply() (bool, error) {
    53  	if nm.err != nil {
    54  		return false, nm.err
    55  	}
    56  	return true, nil
    57  }
    58  
    59  func (nm *netplanMock) Close() error {
    60  	return nil
    61  }
    62  
    63  func (nm *netplanMock) Connect() error {
    64  	return nil
    65  }
    66  
    67  var f f2.Framework
    68  
    69  func TestMain(m *testing.M) {
    70  	f = f2.New(context.Background(), f2.WithExtensions()).
    71  		Setup().
    72  		Teardown()
    73  	os.Exit(f.Run(m))
    74  }
    75  
    76  func TestStaticNodeConfiguration_Egress_Gateway_Disabled(t *testing.T) {
    77  	var kclient client.Client
    78  	var ienode *v1ien.IENode
    79  	var ns netns.NsHandle
    80  	var tearDownFunction func()
    81  	var macAddress string
    82  	const gatewayEnabled = false
    83  
    84  	feature := f2.NewFeature("netplan with static ip configuration").
    85  		Setup("Create fake client", func(ctx f2.Context, t *testing.T) f2.Context {
    86  			ienode = staticIPWorkerNode()
    87  			workerNode := k8sNode(ienode, metav1.NewTime(metav1.Now().Add(time.Duration(2)*time.Minute)))
    88  
    89  			ns, tearDownFunction = testnetns.NewTestingNetworkNamespace(t)
    90  			assert.NoError(t, testnetns.CreateDummyLink())
    91  
    92  			dev, err := testnetns.GetDummyLink()
    93  			assert.NoError(t, err)
    94  
    95  			macAddress = dev.Attrs().HardwareAddr.String()
    96  			ienode.Spec.Network[0].MacAddress = macAddress
    97  			ienode.Spec.PrimaryInterface.MacAddresses = append(ienode.Spec.PrimaryInterface.MacAddresses, macAddress)
    98  
    99  			kclient = fake.NewClientBuilder().WithScheme(createScheme()).WithObjects(workerNode, createTopologyInfoConfigMap(gatewayEnabled)).Build()
   100  			return ctx
   101  		}).
   102  		Test("netplan applies static configuration with no gateway configuration", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl
   103  			assert.NoError(t, netns.Set(ns))
   104  
   105  			memFS, err := createMemFS()
   106  			assert.NoError(t, err)
   107  
   108  			netMock := &netplanMock{}
   109  			cfg := config.NewConfig(kclient, nil, nil, config.Flags{}).WithFs(memFS).WithNetplanAPI(netMock)
   110  
   111  			_, err = Plugin{}.Reconcile(ctx, ienode, cfg)
   112  			assert.NoError(t, err)
   113  
   114  			contents, err := afero.ReadFile(memFS, netplanFile)
   115  			assert.NoError(t, err)
   116  
   117  			networkConfig := Template()
   118  			err = yaml.Unmarshal(contents, networkConfig)
   119  			assert.NoError(t, err)
   120  
   121  			assert.Equal(t, networkConfig.Ethernets[iface].Addresses[0], nodeCIDR)
   122  			assert.Equal(t, networkConfig.Ethernets[iface].Match.MacAddress, macAddress)
   123  			assert.Equal(t, networkConfig.Ethernets[iface].Nameservers.Addresses[0], dns)
   124  			assert.False(t, networkConfig.Ethernets[iface].DHCP4)
   125  			assert.False(t, networkConfig.Ethernets[iface].Routes[0].OnLink)
   126  
   127  			// expect no tunnels when the gateway is disabled
   128  			assert.Equal(t, networkConfig.Tunnels, map[string]Tunnel{})
   129  
   130  			return ctx
   131  		}).
   132  		Test("netplan fails and reverts changes to netplan to original file contents", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl
   133  			assert.NoError(t, netns.Set(ns))
   134  
   135  			memFS, err := createMemFS()
   136  			assert.NoError(t, err)
   137  
   138  			netMock := &netplanMock{err: errors.New("netplan file failed")}
   139  			cfg := config.NewConfig(kclient, nil, nil, config.Flags{}).WithFs(memFS).WithNetplanAPI(netMock)
   140  
   141  			_, err = Plugin{}.Reconcile(ctx, ienode, cfg)
   142  			assert.Error(t, err)
   143  
   144  			contents, err := afero.ReadFile(memFS, netplanFile)
   145  			assert.NoError(t, err)
   146  
   147  			networkConfig := Template()
   148  			err = yaml.Unmarshal(contents, networkConfig)
   149  			assert.NoError(t, err)
   150  
   151  			// original config has no interfaces
   152  			// we expect it to revert to the original netplan file if netplan apply fails
   153  			assert.Equal(t, map[string]Ethernet{}, networkConfig.Ethernets)
   154  
   155  			sysctlRPFilterConfAsExpected(t, memFS, gatewayEnabled, ienode.Spec.IsGatewayNode(), nil)
   156  
   157  			return ctx
   158  		}).
   159  		Teardown("teardown testing namespace", func(ctx f2.Context, _ *testing.T) f2.Context {
   160  			tearDownFunction()
   161  			return ctx
   162  		}).Feature()
   163  
   164  	f.Test(t, feature)
   165  }
   166  
   167  func TestStaticNodeConfiguration_Gateway_Enabled(t *testing.T) {
   168  	var kclient client.Client
   169  	var controlPlaneIENode *v1ien.IENode
   170  	var workerIENode *v1ien.IENode
   171  	var cfg config.Config
   172  	var memFs afero.Fs
   173  	var ns netns.NsHandle
   174  	var tearDownFunction func()
   175  	var macAddress string
   176  	var netServicesCM *v1.ConfigMap
   177  	const gatewayEnabled = true
   178  	feature := f2.NewFeature("netplan with static ip configuration").
   179  		Setup("Create fake client", func(ctx f2.Context, t *testing.T) f2.Context {
   180  			workerIENode = staticIPWorkerNode()
   181  			controlPlaneIENode = staticIPControlPlaneNode()
   182  			controlPlaneNode := k8sNode(controlPlaneIENode, metav1.Now())
   183  			workerNode := k8sNode(workerIENode, metav1.NewTime(metav1.Now().Add(time.Duration(2)*time.Minute)))
   184  			netServicesCM = createNetworkServicesConfigMap()
   185  
   186  			ns, tearDownFunction = testnetns.NewTestingNetworkNamespace(t)
   187  			assert.NoError(t, testnetns.CreateDummyLink())
   188  
   189  			dev, err := testnetns.GetDummyLink()
   190  			assert.NoError(t, err)
   191  
   192  			macAddress = dev.Attrs().HardwareAddr.String()
   193  
   194  			workerIENode.Spec.Network[0].MacAddress = macAddress
   195  			controlPlaneIENode.Spec.Network[0].MacAddress = macAddress
   196  			workerIENode.Spec.PrimaryInterface.MacAddresses = append(workerIENode.Spec.PrimaryInterface.MacAddresses, macAddress)
   197  			controlPlaneIENode.Spec.PrimaryInterface.MacAddresses = append(controlPlaneIENode.Spec.PrimaryInterface.MacAddresses, macAddress)
   198  
   199  			kclient = fake.NewClientBuilder().WithScheme(createScheme()).WithObjects(createTopologyInfoConfigMap(gatewayEnabled), controlPlaneNode, workerNode, netServicesCM).Build()
   200  
   201  			memoryFS, err := createMemFS()
   202  			assert.NoError(t, err)
   203  			memFs = memoryFS
   204  
   205  			cfg = config.NewConfig(kclient, nil, nil, config.Flags{}).WithFs(memFs).WithNetplanAPI(&netplanMock{})
   206  
   207  			return ctx
   208  		}).
   209  		Test("netplan applies static gateway configuration for worker nodes", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl
   210  			assert.NoError(t, netns.Set(ns))
   211  			_, err := Plugin{}.Reconcile(ctx, workerIENode, cfg)
   212  			assert.NoError(t, err)
   213  
   214  			contents, err := afero.ReadFile(memFs, netplanFile)
   215  			assert.NoError(t, err)
   216  
   217  			networkConfig := Template()
   218  			err = yaml.Unmarshal(contents, networkConfig)
   219  			assert.NoError(t, err)
   220  
   221  			assert.Equal(t, networkConfig.Ethernets[iface].Addresses[0], nodeCIDR)
   222  			assert.Equal(t, networkConfig.Ethernets[iface].Nameservers.Addresses[0], dns)
   223  			assert.False(t, networkConfig.Ethernets[iface].DHCP4)
   224  
   225  			assert.Contains(t, networkConfig.Tunnels, "tun1")
   226  			tun := networkConfig.Tunnels["tun1"]
   227  			assert.Equal(t, "192.168.70.3/31", tun.Addresses[0])
   228  			assert.Equal(t, "ipip", tun.Mode)
   229  			assert.Len(t, tun.RoutingPolicies, 1)
   230  			assert.Len(t, tun.Routes, 1)
   231  
   232  			sysctlRPFilterConfAsExpected(t, memFs, gatewayEnabled, workerIENode.Spec.IsGatewayNode(), []string{"tun1"})
   233  
   234  			return ctx
   235  		}).
   236  		Test("netplan applies static gateway configuration for gateway nodes", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl
   237  			assert.NoError(t, netns.Set(ns))
   238  			netServicesCM.Data = map[string]string{}
   239  
   240  			_, err := Plugin{}.Reconcile(ctx, controlPlaneIENode, cfg)
   241  			assert.NoError(t, err)
   242  
   243  			contents, err := afero.ReadFile(memFs, netplanFile)
   244  			assert.NoError(t, err)
   245  
   246  			networkConfig := Template()
   247  			err = yaml.Unmarshal(contents, networkConfig)
   248  			assert.NoError(t, err)
   249  
   250  			assert.Equal(t, networkConfig.Ethernets[iface].Addresses[0], nodeCIDR)
   251  			assert.Equal(t, networkConfig.Ethernets[iface].Match.MacAddress, macAddress)
   252  			assert.Equal(t, networkConfig.Ethernets[iface].Nameservers.Addresses[0], dns)
   253  			assert.False(t, networkConfig.Ethernets[iface].DHCP4)
   254  
   255  			assert.Contains(t, networkConfig.Tunnels, "tun1")
   256  			tun := networkConfig.Tunnels["tun1"]
   257  			assert.Equal(t, "192.168.70.2/31", tun.Addresses[0])
   258  			assert.Equal(t, "ipip", tun.Mode)
   259  			assert.Len(t, tun.RoutingPolicies, 1)
   260  			assert.Len(t, tun.Routes, 1)
   261  
   262  			sysctlRPFilterConfAsExpected(t, memFs, gatewayEnabled, controlPlaneIENode.Spec.IsGatewayNode(), nil)
   263  
   264  			return ctx
   265  		}).
   266  		Teardown("teardown testing namespace", func(ctx f2.Context, _ *testing.T) f2.Context {
   267  			tearDownFunction()
   268  			return ctx
   269  		}).Feature()
   270  
   271  	f.Test(t, feature)
   272  }
   273  
   274  func TestStaticNodeConfiguration_Fallback_CIDR(t *testing.T) {
   275  	var kclient client.Client
   276  	var controlPlaneIENode *v1ien.IENode
   277  	var workerIENode *v1ien.IENode
   278  	var cfg config.Config
   279  	var memFs afero.Fs
   280  	var ns netns.NsHandle
   281  	var tearDownFunction func()
   282  	var macAddress string
   283  	const gatewayEnabled = true
   284  
   285  	feature := f2.NewFeature("netplan with static ip using fallback CIDR").
   286  		Setup("Create fake client", func(ctx f2.Context, t *testing.T) f2.Context {
   287  			// We deliberately skip creating the network services CM to force the fallback
   288  			workerIENode = staticIPWorkerNode()
   289  			controlPlaneIENode = staticIPControlPlaneNode()
   290  			controlPlaneNode := k8sNode(controlPlaneIENode, metav1.Now())
   291  			workerNode := k8sNode(workerIENode, metav1.NewTime(metav1.Now().Add(time.Duration(2)*time.Minute)))
   292  
   293  			ns, tearDownFunction = testnetns.NewTestingNetworkNamespace(t)
   294  			assert.NoError(t, testnetns.CreateDummyLink())
   295  
   296  			dev, err := testnetns.GetDummyLink()
   297  			assert.NoError(t, err)
   298  
   299  			macAddress = dev.Attrs().HardwareAddr.String()
   300  
   301  			workerIENode.Spec.Network[0].MacAddress = macAddress
   302  			controlPlaneIENode.Spec.Network[0].MacAddress = macAddress
   303  			workerIENode.Spec.PrimaryInterface.MacAddresses = append(workerIENode.Spec.PrimaryInterface.MacAddresses, macAddress)
   304  			controlPlaneIENode.Spec.PrimaryInterface.MacAddresses = append(controlPlaneIENode.Spec.PrimaryInterface.MacAddresses, macAddress)
   305  
   306  			kclient = fake.NewClientBuilder().WithScheme(createScheme()).WithObjects(createTopologyInfoConfigMap(gatewayEnabled), controlPlaneNode, workerNode).Build()
   307  
   308  			memoryFS, err := createMemFS()
   309  			assert.NoError(t, err)
   310  			memFs = memoryFS
   311  
   312  			cfg = config.NewConfig(kclient, nil, nil, config.Flags{}).WithFs(memFs).WithNetplanAPI(&netplanMock{})
   313  
   314  			return ctx
   315  		}).
   316  		Test("netplan applies static gateway configuration for worker nodes", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl
   317  			assert.NoError(t, netns.Set(ns))
   318  
   319  			_, err := Plugin{}.Reconcile(ctx, workerIENode, cfg)
   320  			assert.NoError(t, err)
   321  
   322  			contents, err := afero.ReadFile(memFs, netplanFile)
   323  			assert.NoError(t, err)
   324  
   325  			networkConfig := Template()
   326  			err = yaml.Unmarshal(contents, networkConfig)
   327  			assert.NoError(t, err)
   328  
   329  			assert.Contains(t, networkConfig.Tunnels, "tun1")
   330  			tun := networkConfig.Tunnels["tun1"]
   331  			assert.Equal(t, "10.80.0.3/31", tun.Addresses[0])
   332  			assert.Equal(t, "ipip", tun.Mode)
   333  			assert.Len(t, tun.RoutingPolicies, 1)
   334  			assert.Len(t, tun.Routes, 1)
   335  
   336  			sysctlRPFilterConfAsExpected(t, memFs, gatewayEnabled, workerIENode.Spec.IsGatewayNode(), []string{"tun1"})
   337  
   338  			return ctx
   339  		}).
   340  		Teardown("teardown testing namespace", func(ctx f2.Context, _ *testing.T) f2.Context {
   341  			tearDownFunction()
   342  			return ctx
   343  		}).Feature()
   344  
   345  	f.Test(t, feature)
   346  }
   347  
   348  func TestTunnelIPConfiguration(t *testing.T) {
   349  	var numberWorkers = 19
   350  	var controlPlaneIENode *v1ien.IENode
   351  	var nodes = make([]v1.Node, numberWorkers+1)
   352  	var ienNodes = make([]v1ien.IENode, numberWorkers+1)
   353  
   354  	feature := f2.NewFeature("netplan tunnel configuration test with gateway configured").
   355  		Setup("create nodes", func(ctx f2.Context, _ *testing.T) f2.Context {
   356  			controlPlaneIENode = staticIPControlPlaneNode()
   357  			controlPlaneNode := k8sNode(controlPlaneIENode, metav1.Now())
   358  
   359  			nodes[0] = *controlPlaneNode
   360  			ienNodes[0] = *controlPlaneIENode
   361  
   362  			for i := 1; i <= numberWorkers; i++ {
   363  				workerIENode := staticIPWorkerNode()
   364  				workerNode := k8sNode(workerIENode, metav1.NewTime(metav1.Now().Add(time.Duration(2)*time.Minute)))
   365  				nodes[i] = *workerNode
   366  				ienNodes[i] = *workerIENode
   367  			}
   368  
   369  			return ctx
   370  		}).
   371  		Test("20 node egress gateway configuration test", func(ctx f2.Context, t *testing.T) f2.Context { //nolint:dupl
   372  			nodes = sortK8sNodesByCreationTimestamp(nodes)
   373  			gatewayNodeIndexes, nonGatewayNodeIndexes, err := filterGatewayNodeIndexes(nodes)
   374  			assert.NoError(t, err)
   375  
   376  			cpNetplanConfig := Template()
   377  			_, cidr, _ := net.ParseCIDR("10.0.0.65/26")
   378  			subnets, err := internal.PairTunnelIPs(cidr)
   379  			assert.NoError(t, err)
   380  
   381  			cpConfig, err := configureGatewayNodeTunnels(cpNetplanConfig, controlPlaneIENode, nodes, nonGatewayNodeIndexes, subnets)
   382  			assert.NoError(t, err)
   383  
   384  			for idx := range ienNodes {
   385  				if idx == 0 { // idx zero is control plane node
   386  					continue
   387  				}
   388  
   389  				ienNode := &ienNodes[idx]
   390  				workerNetplanConfig := Template()
   391  				workerConfig, err := configureNonGatewayNodeTunnels(workerNetplanConfig, ienNode, nodes, gatewayNodeIndexes, subnets)
   392  				assert.NoError(t, err)
   393  
   394  				tunName := fmt.Sprintf("tun%d", idx)
   395  
   396  				tunnelFromCP, ok := cpConfig.Tunnels[tunName]
   397  				assert.True(t, ok)
   398  				expected, err := expectedTunnelFromCP(subnets[idx])
   399  				assert.NoError(t, err)
   400  				assert.Equal(t, expected, tunnelFromCP)
   401  
   402  				tunnelFromTP, ok := workerConfig.Tunnels[tunName]
   403  				assert.True(t, ok)
   404  				expected, err = expectedTunnelFromTP(subnets[idx])
   405  				assert.NoError(t, err)
   406  				assert.Equal(t, expected, tunnelFromTP)
   407  
   408  				_, pairTunnelSubnet, err := net.ParseCIDR(tunnelFromCP.Addresses[0])
   409  				assert.NoError(t, err)
   410  				// cast the tunnel ip to ipNet
   411  				workerIP, _, err := net.ParseCIDR(tunnelFromTP.Addresses[0])
   412  				assert.NoError(t, err)
   413  				// verify the worker ip exists in same subnet as cp tunnel ip
   414  				assert.True(t, pairTunnelSubnet.Contains(workerIP))
   415  			}
   416  			return ctx
   417  		}).Feature()
   418  
   419  	f.Test(t, feature)
   420  }
   421  
   422  func createMemFS() (afero.Fs, error) {
   423  	fs := afero.NewMemMapFs()
   424  	netplanContents, err := generateNetplanConfig()
   425  	if err != nil {
   426  		return nil, err
   427  	}
   428  	if err := afero.WriteFile(fs, netplanFile, netplanContents, 0644); err != nil {
   429  		return nil, err
   430  	}
   431  	return fs, nil
   432  }
   433  
   434  func createTopologyInfoConfigMap(gatewayEnabled bool) *v1.ConfigMap {
   435  	tpInfo := topology.New()
   436  	tpInfo.EgressGatewayEnabled = gatewayEnabled
   437  	return tpInfo.ToConfigMap()
   438  }
   439  
   440  func createNetworkServicesConfigMap() *v1.ConfigMap {
   441  	return &v1.ConfigMap{
   442  		TypeMeta: metav1.TypeMeta{
   443  			Kind:       "ConfigMap",
   444  			APIVersion: v1.SchemeGroupVersion.String(),
   445  		},
   446  		ObjectMeta: metav1.ObjectMeta{
   447  			Name:      edgeconst.NetworkServicesCM,
   448  			Namespace: edgeconst.SdsNamespace,
   449  		},
   450  		Data: map[string]string{
   451  			edgeconst.ServiceTypeEgressTunnelsCIDR: "192.168.70.0/24",
   452  		},
   453  	}
   454  }
   455  
   456  func generateNetplanConfig() ([]byte, error) {
   457  	conf := Template()
   458  	return formatNetplan(conf)
   459  }
   460  
   461  func staticIPWorkerNode() *v1ien.IENode {
   462  	return &v1ien.IENode{
   463  		ObjectMeta: metav1.ObjectMeta{
   464  			Name: uuid.New().UUID,
   465  		},
   466  		Spec: v1ien.IENodeSpec{
   467  			Role: v1ien.Worker,
   468  			Lane: "1",
   469  			NetworkServices: v1ien.NetworkServices{
   470  				DNSServers: []string{dns},
   471  			},
   472  			PrimaryInterface: &v1ien.PrimaryInterface{
   473  				InterfaceID:  uuid.New().UUID,
   474  				MacAddresses: []string{},
   475  			},
   476  			Network: []v1ien.Network{
   477  				{
   478  					MacAddress: "",
   479  					Addresses:  []string{nodeCIDR},
   480  					Gateway4:   gateway,
   481  					DHCP4:      false,
   482  				},
   483  			},
   484  		},
   485  		Status: &v1ien.IENodeStatus{
   486  			Conditions: []metav1.Condition{
   487  				{
   488  					Type:   "iptables",
   489  					Status: metav1.ConditionTrue,
   490  				},
   491  			},
   492  		},
   493  	}
   494  }
   495  
   496  func staticIPControlPlaneNode() *v1ien.IENode {
   497  	return &v1ien.IENode{
   498  		ObjectMeta: metav1.ObjectMeta{
   499  			Name: uuid.New().UUID,
   500  		},
   501  		Spec: v1ien.IENodeSpec{
   502  			Role: v1ien.ControlPlane,
   503  			Lane: "1",
   504  			NetworkServices: v1ien.NetworkServices{
   505  				DNSServers: []string{dns},
   506  			},
   507  			PrimaryInterface: &v1ien.PrimaryInterface{
   508  				InterfaceID:  uuid.New().UUID,
   509  				MacAddresses: []string{},
   510  			},
   511  			Network: []v1ien.Network{
   512  				{
   513  					MacAddress: "",
   514  					Addresses:  []string{nodeCIDR},
   515  					Gateway4:   gateway,
   516  					DHCP4:      false,
   517  				},
   518  			},
   519  		},
   520  		Status: &v1ien.IENodeStatus{
   521  			Conditions: []metav1.Condition{
   522  				{
   523  					Type:   "iptables",
   524  					Status: metav1.ConditionTrue,
   525  				},
   526  			},
   527  		},
   528  	}
   529  }
   530  
   531  func createScheme() *kruntime.Scheme {
   532  	scheme := kruntime.NewScheme()
   533  	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
   534  	utilruntime.Must(v1.AddToScheme(scheme))
   535  	utilruntime.Must(v1ien.AddToScheme(scheme))
   536  	return scheme
   537  }
   538  
   539  func formatNetplan(netConfig *Config) ([]byte, error) {
   540  	var b bytes.Buffer
   541  	yamlEncoder := yaml.NewEncoder(&b)
   542  	yamlEncoder.SetIndent(2)
   543  	err := yamlEncoder.Encode(netConfig)
   544  	return b.Bytes(), err
   545  }
   546  
   547  func k8sNode(ienode *v1ien.IENode, createdTime metav1.Time) *v1.Node {
   548  	return &v1.Node{
   549  		ObjectMeta: metav1.ObjectMeta{
   550  			Name:              ienode.ObjectMeta.Name,
   551  			CreationTimestamp: createdTime,
   552  			Annotations: map[string]string{
   553  				calico.IPAnnotation: ienode.Spec.Network[0].Addresses[0],
   554  			},
   555  			Labels: map[string]string{
   556  				nodemeta.RoleLabel: string(ienode.Spec.Role),
   557  			},
   558  		},
   559  		Spec: v1.NodeSpec{},
   560  	}
   561  }
   562  
   563  func expectedTunnelFromTP(subnet *net.IPNet) (Tunnel, error) {
   564  	// use second ip for non-gateway nodes
   565  	tunnelIP, err := networking.GetAddressFromSubnet(subnet, 1)
   566  	if err != nil {
   567  		return Tunnel{}, err
   568  	}
   569  
   570  	// use first ip for gateway nodes
   571  	gatewayTunnelCIDR, err := networking.GetAddressFromSubnet(subnet, 0)
   572  	if err != nil {
   573  		return Tunnel{}, err
   574  	}
   575  
   576  	gatewayTunnelIP, _, err := net.ParseCIDR(gatewayTunnelCIDR)
   577  	if err != nil {
   578  		return Tunnel{}, err
   579  	}
   580  	return Tunnel{
   581  		Mode:      "ipip",
   582  		Local:     nodeIP,
   583  		Remote:    nodeIP,
   584  		Addresses: []string{tunnelIP},
   585  		Routes: []Route{
   586  			{
   587  				To:    "default",
   588  				Via:   gatewayTunnelIP.String(),
   589  				Table: routing.EgressGatewayTable,
   590  			},
   591  		},
   592  		RoutingPolicies: []RoutingPolicy{
   593  			{
   594  				Mark:     512,
   595  				To:       "0.0.0.0/0",
   596  				Table:    routing.EgressGatewayTable,
   597  				Priority: 32765,
   598  			},
   599  		},
   600  	}, nil
   601  }
   602  
   603  func expectedTunnelFromCP(subnet *net.IPNet) (Tunnel, error) {
   604  	// use first ip for gateway nodes
   605  	tunnelIP, err := networking.GetAddressFromSubnet(subnet, 0)
   606  	if err != nil {
   607  		return Tunnel{}, err
   608  	}
   609  	return Tunnel{
   610  		Mode:      "ipip",
   611  		Local:     nodeIP,
   612  		Remote:    nodeIP,
   613  		Addresses: []string{tunnelIP},
   614  	}, nil
   615  }
   616  
   617  func sysctlRPFilterConfAsExpected(t *testing.T, fs afero.Fs, gatewayEnabled bool, isGatewayNode bool, ifaceList []string) {
   618  	actual, err := afero.ReadFile(fs, filepath.Join(sysctlDir, rpFilterConf))
   619  	if !gatewayEnabled || isGatewayNode {
   620  		assert.ErrorIs(t, err, os.ErrNotExist)
   621  		assert.Empty(t, ifaceList)
   622  		return
   623  	}
   624  	assert.NoError(t, err)
   625  	expected := generateRPFilterConfig(routing.RPFilterLoose, ifaceList)
   626  	assert.Equal(t, expected, actual)
   627  }
   628  

View as plain text