k8s.io/kubernetes@v1.29.3/pkg/registry/core/node/strategy_test.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package node
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	"k8s.io/apimachinery/pkg/api/resource"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/fields"
    29  	"k8s.io/apimachinery/pkg/labels"
    30  	apitesting "k8s.io/kubernetes/pkg/api/testing"
    31  	api "k8s.io/kubernetes/pkg/apis/core"
    32  
    33  	// ensure types are installed
    34  	_ "k8s.io/kubernetes/pkg/apis/core/install"
    35  )
    36  
    37  func TestMatchNode(t *testing.T) {
    38  	testFieldMap := map[bool][]fields.Set{
    39  		true: {
    40  			{"metadata.name": "foo"},
    41  		},
    42  		false: {
    43  			{"foo": "bar"},
    44  		},
    45  	}
    46  
    47  	for expectedResult, fieldSet := range testFieldMap {
    48  		for _, field := range fieldSet {
    49  			m := MatchNode(labels.Everything(), field.AsSelector())
    50  			_, matchesSingle := m.MatchesSingle()
    51  			if e, a := expectedResult, matchesSingle; e != a {
    52  				t.Errorf("%+v: expected %v, got %v", fieldSet, e, a)
    53  			}
    54  		}
    55  	}
    56  }
    57  
    58  func TestSelectableFieldLabelConversions(t *testing.T) {
    59  	apitesting.TestSelectableFieldLabelConversionsOfKind(t,
    60  		"v1",
    61  		"Node",
    62  		NodeToSelectableFields(&api.Node{}),
    63  		nil,
    64  	)
    65  }
    66  
    67  // helper creates a NodeNode with a set of PodCIDRs, Spec.ConfigSource, Status.Config
    68  func makeNode(podCIDRs []string, addSpecDynamicConfig bool, addStatusDynamicConfig bool) *api.Node {
    69  	node := &api.Node{
    70  		Spec: api.NodeSpec{
    71  			PodCIDRs: podCIDRs,
    72  		},
    73  	}
    74  	if addSpecDynamicConfig {
    75  		node.Spec.ConfigSource = &api.NodeConfigSource{}
    76  	}
    77  	if addStatusDynamicConfig {
    78  		node.Status = api.NodeStatus{
    79  			Config: &api.NodeConfigStatus{},
    80  		}
    81  	}
    82  
    83  	return node
    84  }
    85  
    86  func TestDropFields(t *testing.T) {
    87  	testCases := []struct {
    88  		name        string
    89  		node        *api.Node
    90  		oldNode     *api.Node
    91  		compareNode *api.Node
    92  	}{
    93  		{
    94  			name:        "nil pod cidrs",
    95  			node:        makeNode(nil, false, false),
    96  			oldNode:     nil,
    97  			compareNode: makeNode(nil, false, false),
    98  		},
    99  		{
   100  			name:        "empty pod ips",
   101  			node:        makeNode([]string{}, false, false),
   102  			oldNode:     nil,
   103  			compareNode: makeNode([]string{}, false, false),
   104  		},
   105  		{
   106  			name:        "single family ipv6",
   107  			node:        makeNode([]string{"2000::/10"}, false, false),
   108  			compareNode: makeNode([]string{"2000::/10"}, false, false),
   109  		},
   110  		{
   111  			name:        "single family ipv4",
   112  			node:        makeNode([]string{"10.0.0.0/8"}, false, false),
   113  			compareNode: makeNode([]string{"10.0.0.0/8"}, false, false),
   114  		},
   115  		{
   116  			name:        "dualstack 4-6",
   117  			node:        makeNode([]string{"10.0.0.0/8", "2000::/10"}, false, false),
   118  			compareNode: makeNode([]string{"10.0.0.0/8", "2000::/10"}, false, false),
   119  		},
   120  		{
   121  			name:        "dualstack 6-4",
   122  			node:        makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
   123  			compareNode: makeNode([]string{"2000::/10", "10.0.0.0/8"}, false, false),
   124  		},
   125  		{
   126  			name:        "new with no Spec.ConfigSource and no Status.Config",
   127  			node:        makeNode(nil, false, false),
   128  			oldNode:     nil,
   129  			compareNode: makeNode(nil, false, false),
   130  		},
   131  		{
   132  			name:        "new with Spec.ConfigSource and no Status.Config",
   133  			node:        makeNode(nil, true, false),
   134  			oldNode:     nil,
   135  			compareNode: makeNode(nil, false, false),
   136  		},
   137  		{
   138  			name:        "new with Spec.ConfigSource and Status.Config",
   139  			node:        makeNode(nil, true, true),
   140  			oldNode:     nil,
   141  			compareNode: makeNode(nil, false, false),
   142  		},
   143  		{
   144  			name:        "update with Spec.ConfigSource and Status.Config (old has none)",
   145  			node:        makeNode(nil, true, true),
   146  			oldNode:     makeNode(nil, false, false),
   147  			compareNode: makeNode(nil, false, true),
   148  		},
   149  		{
   150  			name:        "update with Spec.ConfigSource and Status.Config (old has them)",
   151  			node:        makeNode(nil, true, true),
   152  			oldNode:     makeNode(nil, true, true),
   153  			compareNode: makeNode(nil, true, true),
   154  		},
   155  		{
   156  			name:        "update with Spec.ConfigSource and Status.Config (old has Status.Config)",
   157  			node:        makeNode(nil, true, true),
   158  			oldNode:     makeNode(nil, false, true),
   159  			compareNode: makeNode(nil, false, true),
   160  		},
   161  	}
   162  
   163  	for _, tc := range testCases {
   164  		func() {
   165  			dropDisabledFields(tc.node, tc.oldNode)
   166  
   167  			old := tc.oldNode.DeepCopy()
   168  			// old node  should never be changed
   169  			if !reflect.DeepEqual(tc.oldNode, old) {
   170  				t.Errorf("%v: old node changed: %v", tc.name, cmp.Diff(tc.oldNode, old))
   171  			}
   172  
   173  			if !reflect.DeepEqual(tc.node, tc.compareNode) {
   174  				t.Errorf("%v: unexpected node spec: %v", tc.name, cmp.Diff(tc.node, tc.compareNode))
   175  			}
   176  		}()
   177  	}
   178  }
   179  func TestValidateUpdate(t *testing.T) {
   180  	tests := []struct {
   181  		oldNode api.Node
   182  		node    api.Node
   183  		valid   bool
   184  	}{
   185  		{api.Node{
   186  			ObjectMeta: metav1.ObjectMeta{
   187  				Name: "hugepage-change-values-from-0",
   188  			},
   189  			Status: api.NodeStatus{
   190  				Capacity: api.ResourceList{
   191  					api.ResourceName("hugepages-2Mi"): resource.MustParse("0"),
   192  					api.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
   193  				},
   194  			},
   195  		}, api.Node{
   196  			ObjectMeta: metav1.ObjectMeta{
   197  				Name: "hugepage-change-values-from-0",
   198  			},
   199  			Status: api.NodeStatus{
   200  				Capacity: api.ResourceList{
   201  					api.ResourceName("hugepages-2Mi"): resource.MustParse("2Gi"),
   202  					api.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
   203  				},
   204  			},
   205  		}, true},
   206  		{api.Node{
   207  			ObjectMeta: metav1.ObjectMeta{
   208  				Name: "hugepage-change-values",
   209  			},
   210  			Status: api.NodeStatus{
   211  				Capacity: api.ResourceList{
   212  					api.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
   213  					api.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
   214  				},
   215  			},
   216  		}, api.Node{
   217  			ObjectMeta: metav1.ObjectMeta{
   218  				Name: "hugepage-change-values",
   219  			},
   220  			Status: api.NodeStatus{
   221  				Capacity: api.ResourceList{
   222  					api.ResourceName("hugepages-2Mi"): resource.MustParse("2Gi"),
   223  					api.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
   224  				},
   225  			},
   226  		}, true},
   227  	}
   228  	for i, test := range tests {
   229  		test.node.ObjectMeta.ResourceVersion = "1"
   230  		errs := (nodeStrategy{}).ValidateUpdate(context.Background(), &test.node, &test.oldNode)
   231  		if test.valid && len(errs) > 0 {
   232  			t.Errorf("%d: Unexpected error: %v", i, errs)
   233  			t.Logf("%#v vs %#v", test.oldNode.ObjectMeta, test.node.ObjectMeta)
   234  		}
   235  		if !test.valid && len(errs) == 0 {
   236  			t.Errorf("%d: Unexpected non-error", i)
   237  		}
   238  	}
   239  }
   240  func TestValidate(t *testing.T) {
   241  	tests := []struct {
   242  		node  api.Node
   243  		valid bool
   244  	}{
   245  		{api.Node{
   246  			ObjectMeta: metav1.ObjectMeta{
   247  				Name: "one-hugepage-size",
   248  			},
   249  			Status: api.NodeStatus{
   250  				Capacity: api.ResourceList{
   251  					api.ResourceCPU:                   resource.MustParse("100"),
   252  					api.ResourceMemory:                resource.MustParse("10000"),
   253  					api.ResourceName("hugepages-2Mi"): resource.MustParse("0"),
   254  					api.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
   255  				},
   256  			},
   257  		}, true},
   258  		{api.Node{
   259  			ObjectMeta: metav1.ObjectMeta{
   260  				Name: "multiple-hugepage-sizes",
   261  			},
   262  			Status: api.NodeStatus{
   263  				Capacity: api.ResourceList{
   264  					api.ResourceCPU:                   resource.MustParse("100"),
   265  					api.ResourceMemory:                resource.MustParse("10000"),
   266  					api.ResourceName("hugepages-2Mi"): resource.MustParse("2Gi"),
   267  					api.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
   268  				},
   269  			},
   270  		}, true},
   271  	}
   272  	for i, test := range tests {
   273  		test.node.ObjectMeta.ResourceVersion = "1"
   274  		errs := (nodeStrategy{}).Validate(context.Background(), &test.node)
   275  		if test.valid && len(errs) > 0 {
   276  			t.Errorf("%d: Unexpected error: %v", i, errs)
   277  		}
   278  		if !test.valid && len(errs) == 0 {
   279  			t.Errorf("%d: Unexpected non-error", i)
   280  		}
   281  	}
   282  }
   283  
   284  func TestWarningOnUpdateAndCreate(t *testing.T) {
   285  	tests := []struct {
   286  		oldNode     api.Node
   287  		node        api.Node
   288  		warningText string
   289  	}{
   290  		{api.Node{},
   291  			api.Node{},
   292  			""},
   293  		{api.Node{},
   294  			api.Node{Spec: api.NodeSpec{ConfigSource: &api.NodeConfigSource{}}},
   295  			"spec.configSource"},
   296  		{api.Node{Spec: api.NodeSpec{ConfigSource: &api.NodeConfigSource{}}},
   297  			api.Node{Spec: api.NodeSpec{ConfigSource: &api.NodeConfigSource{}}},
   298  			"spec.configSource"},
   299  		{api.Node{Spec: api.NodeSpec{ConfigSource: &api.NodeConfigSource{}}},
   300  			api.Node{}, ""},
   301  		{api.Node{},
   302  			api.Node{Spec: api.NodeSpec{DoNotUseExternalID: "externalID"}},
   303  			"spec.externalID"},
   304  		{api.Node{Spec: api.NodeSpec{DoNotUseExternalID: "externalID"}},
   305  			api.Node{Spec: api.NodeSpec{DoNotUseExternalID: "externalID"}},
   306  			"spec.externalID"},
   307  		{api.Node{Spec: api.NodeSpec{DoNotUseExternalID: "externalID"}},
   308  			api.Node{}, ""},
   309  	}
   310  	for i, test := range tests {
   311  		warnings := (nodeStrategy{}).WarningsOnUpdate(context.Background(), &test.node, &test.oldNode)
   312  		if (test.warningText != "" && len(warnings) != 1) || (test.warningText == "" && len(warnings) != 0) {
   313  			t.Errorf("%d: Unexpected warnings count: %v", i, warnings)
   314  			t.Logf("%#v vs %#v", test.oldNode.ObjectMeta, test.node.ObjectMeta)
   315  		} else if test.warningText != "" && !strings.Contains(warnings[0], test.warningText) {
   316  			t.Errorf("%d: Wrong warning message: %v", i, warnings[0])
   317  		}
   318  
   319  		warnings = (nodeStrategy{}).WarningsOnCreate(context.Background(), &test.node)
   320  		if (test.warningText != "" && len(warnings) != 1) || (test.warningText == "" && len(warnings) != 0) {
   321  			t.Errorf("%d: Unexpected warnings count: %v", i, warnings)
   322  			t.Logf("%#v vs %#v", test.oldNode.ObjectMeta, test.node.ObjectMeta)
   323  		} else if test.warningText != "" && !strings.Contains(warnings[0], test.warningText) {
   324  			t.Errorf("%d: Wrong warning message: %v", i, warnings[0])
   325  		}
   326  	}
   327  }