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 }