github.com/cilium/cilium@v1.16.2/pkg/k8s/node_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package k8s
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/cilium/cilium/pkg/annotation"
    12  	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
    13  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    14  	nodeAddressing "github.com/cilium/cilium/pkg/node/addressing"
    15  	"github.com/cilium/cilium/pkg/option"
    16  	"github.com/cilium/cilium/pkg/source"
    17  )
    18  
    19  func TestParseNode(t *testing.T) {
    20  	prevAnnotateK8sNode := option.Config.AnnotateK8sNode
    21  	option.Config.AnnotateK8sNode = true
    22  	defer func() {
    23  		option.Config.AnnotateK8sNode = prevAnnotateK8sNode
    24  	}()
    25  
    26  	// PodCIDR takes precedence over annotations
    27  	k8sNode := &slim_corev1.Node{
    28  		ObjectMeta: slim_metav1.ObjectMeta{
    29  			Name: "node1",
    30  			Annotations: map[string]string{
    31  				annotation.V4CIDRName:     "10.254.0.0/16",
    32  				annotation.V6CIDRName:     "f00d:aaaa:bbbb:cccc:dddd:eeee::/112",
    33  				annotation.CiliumHostIP:   "10.254.9.9",
    34  				annotation.CiliumHostIPv6: "fd00:10:244:1::8ace",
    35  				"cilium.io/foo":           "value",
    36  				"qux.cilium.io/foo":       "value",
    37  				"fr3d.qux.cilium.io/foo":  "value",
    38  				"other.whatever.io/foo":   "value",
    39  			},
    40  			Labels: map[string]string{
    41  				"type": "m5.xlarge",
    42  			},
    43  		},
    44  		Spec: slim_corev1.NodeSpec{
    45  			PodCIDR: "10.1.0.0/16",
    46  		},
    47  	}
    48  
    49  	n := ParseNode(k8sNode, source.Local)
    50  	require.Equal(t, "node1", n.Name)
    51  	require.NotNil(t, n.IPv4AllocCIDR)
    52  	require.Equal(t, "10.1.0.0/16", n.IPv4AllocCIDR.String())
    53  	require.NotNil(t, n.IPv6AllocCIDR)
    54  	require.Equal(t, "f00d:aaaa:bbbb:cccc:dddd:eeee::/112", n.IPv6AllocCIDR.String())
    55  	require.Equal(t, "m5.xlarge", n.Labels["type"])
    56  	require.Equal(t, 2, len(n.IPAddresses))
    57  	require.Equal(t, "10.254.9.9", n.IPAddresses[0].IP.String())
    58  	require.Equal(t, nodeAddressing.NodeCiliumInternalIP, n.IPAddresses[0].Type)
    59  	require.Equal(t, "fd00:10:244:1::8ace", n.IPAddresses[1].IP.String())
    60  	require.Equal(t, nodeAddressing.NodeCiliumInternalIP, n.IPAddresses[1].Type)
    61  
    62  	for _, key := range []string{"cilium.io/foo", "qux.cilium.io/foo", "fr3d.qux.cilium.io/foo"} {
    63  		require.Equal(t, "value", n.Annotations[key])
    64  	}
    65  	require.NotContains(t, n.Annotations, "other.whatever.io/foo")
    66  
    67  	// No IPv6 annotation
    68  	k8sNode = &slim_corev1.Node{
    69  		ObjectMeta: slim_metav1.ObjectMeta{
    70  			Name: "node2",
    71  			Annotations: map[string]string{
    72  				annotation.V4CIDRName: "10.254.0.0/16",
    73  			},
    74  		},
    75  		Spec: slim_corev1.NodeSpec{
    76  			PodCIDR: "10.1.0.0/16",
    77  		},
    78  	}
    79  
    80  	n = ParseNode(k8sNode, source.Local)
    81  	require.Equal(t, "node2", n.Name)
    82  	require.NotNil(t, n.IPv4AllocCIDR)
    83  	require.Equal(t, "10.1.0.0/16", n.IPv4AllocCIDR.String())
    84  	require.Nil(t, n.IPv6AllocCIDR)
    85  
    86  	// No IPv6 annotation but PodCIDR with v6
    87  	k8sNode = &slim_corev1.Node{
    88  		ObjectMeta: slim_metav1.ObjectMeta{
    89  			Name: "node2",
    90  			Annotations: map[string]string{
    91  				annotation.V4CIDRName: "10.254.0.0/16",
    92  			},
    93  		},
    94  		Spec: slim_corev1.NodeSpec{
    95  			PodCIDR: "f00d:aaaa:bbbb:cccc:dddd:eeee::/112",
    96  		},
    97  	}
    98  
    99  	n = ParseNode(k8sNode, source.Local)
   100  	require.Equal(t, "node2", n.Name)
   101  	require.NotNil(t, n.IPv4AllocCIDR)
   102  	require.Equal(t, "10.254.0.0/16", n.IPv4AllocCIDR.String())
   103  	require.NotNil(t, n.IPv6AllocCIDR)
   104  	require.Equal(t, "f00d:aaaa:bbbb:cccc:dddd:eeee::/112", n.IPv6AllocCIDR.String())
   105  
   106  	// No IPv4/IPv6 annotations but PodCIDRs with IPv4/IPv6
   107  	k8sNode = &slim_corev1.Node{
   108  		ObjectMeta: slim_metav1.ObjectMeta{
   109  			Name: "node2",
   110  			Annotations: map[string]string{
   111  				annotation.V4CIDRName: "10.254.0.0/16",
   112  			},
   113  		},
   114  		Spec: slim_corev1.NodeSpec{
   115  			PodCIDR:  "10.1.0.0/16",
   116  			PodCIDRs: []string{"10.1.0.0/16", "f00d:aaaa:bbbb:cccc:dddd:eeee::/112"},
   117  		},
   118  	}
   119  
   120  	n = ParseNode(k8sNode, source.Local)
   121  	require.Equal(t, "node2", n.Name)
   122  	require.NotNil(t, n.IPv4AllocCIDR)
   123  	require.Equal(t, "10.1.0.0/16", n.IPv4AllocCIDR.String())
   124  	require.NotNil(t, n.IPv6AllocCIDR)
   125  	require.Equal(t, "f00d:aaaa:bbbb:cccc:dddd:eeee::/112", n.IPv6AllocCIDR.String())
   126  
   127  	// Node with multiple status addresses of the same type and family
   128  	expected := []string{"1.2.3.4", "f00d:aaaa:bbbb:cccc:dddd:eeee:0:1", "4.3.2.1", "f00d:aaaa:bbbb:cccc:dddd:eeef:0:1"}
   129  	notExpected := []string{"5.6.7.8", "f00d:aaaa:bbbb:cccc:dddd:aaaa::1", "8.7.6.5", "f00d:aaaa:bbbb:cccc:dddd:aaab::1"}
   130  	k8sNode = &slim_corev1.Node{
   131  		ObjectMeta: slim_metav1.ObjectMeta{
   132  			Name:        "node2",
   133  			Annotations: map[string]string{},
   134  		},
   135  		Spec: slim_corev1.NodeSpec{
   136  			PodCIDR: "10.1.0.0/16",
   137  		},
   138  		Status: slim_corev1.NodeStatus{
   139  			Addresses: []slim_corev1.NodeAddress{
   140  				{
   141  					Type:    slim_corev1.NodeInternalIP,
   142  					Address: expected[0],
   143  				},
   144  				{
   145  					Type:    slim_corev1.NodeInternalIP,
   146  					Address: notExpected[0],
   147  				},
   148  				{
   149  					Type:    slim_corev1.NodeInternalIP,
   150  					Address: expected[1],
   151  				},
   152  				{
   153  					Type:    slim_corev1.NodeInternalIP,
   154  					Address: notExpected[1],
   155  				},
   156  				{
   157  					Type:    slim_corev1.NodeExternalIP,
   158  					Address: expected[2],
   159  				},
   160  				{
   161  					Type:    slim_corev1.NodeExternalIP,
   162  					Address: notExpected[2],
   163  				},
   164  				{
   165  					Type:    slim_corev1.NodeExternalIP,
   166  					Address: expected[3],
   167  				},
   168  				{
   169  					Type:    slim_corev1.NodeExternalIP,
   170  					Address: notExpected[3],
   171  				},
   172  			},
   173  		},
   174  	}
   175  
   176  	n = ParseNode(k8sNode, source.Local)
   177  	require.Equal(t, "node2", n.Name)
   178  	require.NotNil(t, n.IPv4AllocCIDR)
   179  	require.Equal(t, "10.1.0.0/16", n.IPv4AllocCIDR.String())
   180  	require.Equal(t, len(expected), len(n.IPAddresses))
   181  	addrsFound := 0
   182  	for _, addr := range n.IPAddresses {
   183  		for _, expect := range expected {
   184  			if addr.IP.String() == expect {
   185  				addrsFound++
   186  			}
   187  		}
   188  	}
   189  	require.Equal(t, len(expected), addrsFound)
   190  }
   191  
   192  func TestParseNodeWithoutAnnotations(t *testing.T) {
   193  	prevAnnotateK8sNode := option.Config.AnnotateK8sNode
   194  	option.Config.AnnotateK8sNode = false
   195  	defer func() {
   196  		option.Config.AnnotateK8sNode = prevAnnotateK8sNode
   197  	}()
   198  
   199  	// PodCIDR takes precedence over annotations
   200  	k8sNode := &slim_corev1.Node{
   201  		ObjectMeta: slim_metav1.ObjectMeta{
   202  			Name: "node1",
   203  			Annotations: map[string]string{
   204  				annotation.V4CIDRName:    "10.254.0.0/16",
   205  				annotation.V6CIDRName:    "f00d:aaaa:bbbb:cccc:dddd:eeee::/112",
   206  				"cilium.io/foo":          "value",
   207  				"qux.cilium.io/foo":      "value",
   208  				"fr3d.qux.cilium.io/foo": "value",
   209  				"other.whatever.io/foo":  "value",
   210  			},
   211  			Labels: map[string]string{
   212  				"type": "m5.xlarge",
   213  			},
   214  		},
   215  		Spec: slim_corev1.NodeSpec{
   216  			PodCIDR: "10.1.0.0/16",
   217  		},
   218  	}
   219  
   220  	n := ParseNode(k8sNode, source.Local)
   221  	require.Equal(t, "node1", n.Name)
   222  	require.NotNil(t, n.IPv4AllocCIDR)
   223  	require.Equal(t, "10.1.0.0/16", n.IPv4AllocCIDR.String())
   224  	require.Nil(t, n.IPv6AllocCIDR)
   225  	require.Equal(t, "m5.xlarge", n.Labels["type"])
   226  
   227  	for _, key := range []string{"cilium.io/foo", "qux.cilium.io/foo", "fr3d.qux.cilium.io/foo"} {
   228  		require.Equal(t, "value", n.Annotations[key])
   229  	}
   230  	require.NotContains(t, n.Annotations, "other.whatever.io/foo")
   231  
   232  	// No IPv6 annotation but PodCIDR with v6
   233  	k8sNode = &slim_corev1.Node{
   234  		ObjectMeta: slim_metav1.ObjectMeta{
   235  			Name: "node2",
   236  			Annotations: map[string]string{
   237  				annotation.V4CIDRName: "10.254.0.0/16",
   238  			},
   239  		},
   240  		Spec: slim_corev1.NodeSpec{
   241  			PodCIDR: "f00d:aaaa:bbbb:cccc:dddd:eeee::/112",
   242  		},
   243  	}
   244  
   245  	n = ParseNode(k8sNode, source.Local)
   246  	require.Equal(t, "node2", n.Name)
   247  	require.Nil(t, n.IPv4AllocCIDR)
   248  	require.NotNil(t, n.IPv6AllocCIDR)
   249  	require.Equal(t, "f00d:aaaa:bbbb:cccc:dddd:eeee::/112", n.IPv6AllocCIDR.String())
   250  }
   251  
   252  func Test_ParseNodeAddressType(t *testing.T) {
   253  	type args struct {
   254  		k8sNodeType slim_corev1.NodeAddressType
   255  	}
   256  
   257  	type result struct {
   258  		ciliumNodeType nodeAddressing.AddressType
   259  		errExists      bool
   260  	}
   261  
   262  	tests := []struct {
   263  		name string
   264  		args args
   265  		want result
   266  	}{
   267  		{
   268  			name: "NodeExternalDNS",
   269  			args: args{
   270  				k8sNodeType: slim_corev1.NodeExternalDNS,
   271  			},
   272  			want: result{
   273  				ciliumNodeType: nodeAddressing.NodeExternalDNS,
   274  				errExists:      false,
   275  			},
   276  		},
   277  		{
   278  			name: "NodeExternalIP",
   279  			args: args{
   280  				k8sNodeType: slim_corev1.NodeExternalIP,
   281  			},
   282  			want: result{
   283  				ciliumNodeType: nodeAddressing.NodeExternalIP,
   284  				errExists:      false,
   285  			},
   286  		},
   287  		{
   288  			name: "NodeHostName",
   289  			args: args{
   290  				k8sNodeType: slim_corev1.NodeHostName,
   291  			},
   292  			want: result{
   293  				ciliumNodeType: nodeAddressing.NodeHostName,
   294  				errExists:      false,
   295  			},
   296  		},
   297  		{
   298  			name: "NodeInternalIP",
   299  			args: args{
   300  				k8sNodeType: slim_corev1.NodeInternalIP,
   301  			},
   302  			want: result{
   303  				ciliumNodeType: nodeAddressing.NodeInternalIP,
   304  				errExists:      false,
   305  			},
   306  		},
   307  		{
   308  			name: "NodeInternalDNS",
   309  			args: args{
   310  				k8sNodeType: slim_corev1.NodeInternalDNS,
   311  			},
   312  			want: result{
   313  				ciliumNodeType: nodeAddressing.NodeInternalDNS,
   314  				errExists:      false,
   315  			},
   316  		},
   317  		{
   318  			name: "invalid",
   319  			args: args{
   320  				k8sNodeType: slim_corev1.NodeAddressType("lololol"),
   321  			},
   322  			want: result{
   323  				ciliumNodeType: nodeAddressing.AddressType("lololol"),
   324  				errExists:      true,
   325  			},
   326  		},
   327  	}
   328  
   329  	for _, tt := range tests {
   330  		t.Run(tt.name, func(t *testing.T) {
   331  			gotNodeAddress, gotErr := ParseNodeAddressType(tt.args.k8sNodeType)
   332  			res := result{
   333  				ciliumNodeType: gotNodeAddress,
   334  				errExists:      gotErr != nil,
   335  			}
   336  			require.EqualValues(t, tt.want, res)
   337  		})
   338  	}
   339  }