k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/plugin/pkg/admission/noderestriction/admission_test.go (about) 1 /* 2 Copyright 2017 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 noderestriction 18 19 import ( 20 "context" 21 "reflect" 22 "strings" 23 "testing" 24 "time" 25 26 "k8s.io/apiserver/pkg/util/feature" 27 featuregatetesting "k8s.io/component-base/featuregate/testing" 28 "k8s.io/kubernetes/pkg/features" 29 30 corev1 "k8s.io/api/core/v1" 31 "k8s.io/apimachinery/pkg/api/resource" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/runtime" 34 "k8s.io/apimachinery/pkg/runtime/schema" 35 "k8s.io/apimachinery/pkg/types" 36 "k8s.io/apimachinery/pkg/util/sets" 37 "k8s.io/apiserver/pkg/admission" 38 "k8s.io/apiserver/pkg/authentication/user" 39 corev1lister "k8s.io/client-go/listers/core/v1" 40 "k8s.io/client-go/tools/cache" 41 "k8s.io/component-base/featuregate" 42 kubeletapis "k8s.io/kubelet/pkg/apis" 43 authenticationapi "k8s.io/kubernetes/pkg/apis/authentication" 44 "k8s.io/kubernetes/pkg/apis/coordination" 45 api "k8s.io/kubernetes/pkg/apis/core" 46 "k8s.io/kubernetes/pkg/apis/policy" 47 resourceapi "k8s.io/kubernetes/pkg/apis/resource" 48 storage "k8s.io/kubernetes/pkg/apis/storage" 49 "k8s.io/kubernetes/pkg/auth/nodeidentifier" 50 "k8s.io/utils/pointer" 51 ) 52 53 func makeTestPod(namespace, name, node string, mirror bool) (*api.Pod, *corev1.Pod) { 54 corePod := &api.Pod{} 55 corePod.Namespace = namespace 56 corePod.UID = types.UID("pod-uid") 57 corePod.Name = name 58 corePod.Spec.NodeName = node 59 v1Pod := &corev1.Pod{} 60 v1Pod.Namespace = namespace 61 v1Pod.UID = types.UID("pod-uid") 62 v1Pod.Name = name 63 v1Pod.Spec.NodeName = node 64 if mirror { 65 corePod.Annotations = map[string]string{api.MirrorPodAnnotationKey: "true"} 66 v1Pod.Annotations = map[string]string{api.MirrorPodAnnotationKey: "true"} 67 68 // Insert a valid owner reference by default. 69 controller := true 70 owner := metav1.OwnerReference{ 71 APIVersion: "v1", 72 Kind: "Node", 73 Name: node, 74 UID: types.UID(node + "-uid"), 75 Controller: &controller, 76 } 77 corePod.OwnerReferences = []metav1.OwnerReference{owner} 78 v1Pod.OwnerReferences = []metav1.OwnerReference{owner} 79 } 80 return corePod, v1Pod 81 } 82 83 func withLabels(pod *api.Pod, labels map[string]string) *api.Pod { 84 labeledPod := pod.DeepCopy() 85 if labels == nil { 86 labeledPod.Labels = nil 87 return labeledPod 88 } 89 // Clone. 90 labeledPod.Labels = map[string]string{} 91 for key, value := range labels { 92 labeledPod.Labels[key] = value 93 } 94 return labeledPod 95 } 96 97 func makeTestPodEviction(name string) *policy.Eviction { 98 eviction := &policy.Eviction{} 99 eviction.Name = name 100 eviction.Namespace = "ns" 101 return eviction 102 } 103 104 func makeTokenRequest(podname string, poduid types.UID) *authenticationapi.TokenRequest { 105 tr := &authenticationapi.TokenRequest{ 106 Spec: authenticationapi.TokenRequestSpec{ 107 Audiences: []string{"foo"}, 108 }, 109 } 110 if podname != "" { 111 tr.Spec.BoundObjectRef = &authenticationapi.BoundObjectReference{ 112 Kind: "Pod", 113 APIVersion: "v1", 114 Name: podname, 115 UID: poduid, 116 } 117 } 118 return tr 119 } 120 121 func setAllLabels(node *api.Node, value string) *api.Node { 122 node = setAllowedCreateLabels(node, value) 123 node = setAllowedUpdateLabels(node, value) 124 node = setForbiddenCreateLabels(node, value) 125 node = setForbiddenUpdateLabels(node, value) 126 return node 127 } 128 129 func setAllowedCreateLabels(node *api.Node, value string) *api.Node { 130 node = setAllowedUpdateLabels(node, value) 131 return node 132 } 133 134 func setAllowedUpdateLabels(node *api.Node, value string) *api.Node { 135 node = node.DeepCopy() 136 if node.Labels == nil { 137 node.Labels = map[string]string{} 138 } 139 if value == "" { 140 value = "value" 141 } 142 // non-kube labels 143 node.Labels["foo"] = value 144 node.Labels["example.com/foo"] = value 145 146 // kubelet labels 147 node.Labels["kubernetes.io/hostname"] = value 148 node.Labels["failure-domain.beta.kubernetes.io/zone"] = value 149 node.Labels["failure-domain.beta.kubernetes.io/region"] = value 150 node.Labels["topology.kubernetes.io/zone"] = value 151 node.Labels["topology.kubernetes.io/region"] = value 152 node.Labels["beta.kubernetes.io/instance-type"] = value 153 node.Labels["node.kubernetes.io/instance-type"] = value 154 node.Labels["beta.kubernetes.io/os"] = value 155 node.Labels["beta.kubernetes.io/arch"] = value 156 node.Labels["kubernetes.io/os"] = value 157 node.Labels["kubernetes.io/arch"] = value 158 159 // kubelet label prefixes 160 node.Labels["kubelet.kubernetes.io/foo"] = value 161 node.Labels["foo.kubelet.kubernetes.io/foo"] = value 162 node.Labels["node.kubernetes.io/foo"] = value 163 node.Labels["foo.node.kubernetes.io/foo"] = value 164 165 // test all explicitly allowed labels and prefixes 166 for _, key := range kubeletapis.KubeletLabels() { 167 node.Labels[key] = value 168 } 169 for _, namespace := range kubeletapis.KubeletLabelNamespaces() { 170 node.Labels[namespace+"/foo"] = value 171 node.Labels["foo."+namespace+"/foo"] = value 172 } 173 174 return node 175 } 176 177 func setForbiddenCreateLabels(node *api.Node, value string) *api.Node { 178 node = node.DeepCopy() 179 if node.Labels == nil { 180 node.Labels = map[string]string{} 181 } 182 if value == "" { 183 value = "value" 184 } 185 // node restriction labels are forbidden 186 node.Labels["node-restriction.kubernetes.io/foo"] = value 187 node.Labels["foo.node-restriction.kubernetes.io/foo"] = value 188 node.Labels["other.kubernetes.io/foo"] = value 189 node.Labels["other.k8s.io/foo"] = value 190 return node 191 } 192 193 func setForbiddenUpdateLabels(node *api.Node, value string) *api.Node { 194 node = node.DeepCopy() 195 if node.Labels == nil { 196 node.Labels = map[string]string{} 197 } 198 if value == "" { 199 value = "value" 200 } 201 // node restriction labels are forbidden 202 node.Labels["node-restriction.kubernetes.io/foo"] = value 203 node.Labels["foo.node-restriction.kubernetes.io/foo"] = value 204 // arbitrary kubernetes labels are forbidden on update 205 node.Labels["other.kubernetes.io/foo"] = value 206 node.Labels["other.k8s.io/foo"] = value 207 return node 208 } 209 210 type admitTestCase struct { 211 name string 212 podsGetter corev1lister.PodLister 213 nodesGetter corev1lister.NodeLister 214 attributes admission.Attributes 215 features featuregate.FeatureGate 216 err string 217 } 218 219 func (a *admitTestCase) run(t *testing.T) { 220 t.Run(a.name, func(t *testing.T) { 221 c := NewPlugin(nodeidentifier.NewDefaultNodeIdentifier()) 222 if a.features != nil { 223 c.InspectFeatureGates(a.features) 224 } 225 c.podsGetter = a.podsGetter 226 c.nodesGetter = a.nodesGetter 227 err := c.Admit(context.TODO(), a.attributes, nil) 228 if (err == nil) != (len(a.err) == 0) { 229 t.Errorf("nodePlugin.Admit() error = %v, expected %v", err, a.err) 230 return 231 } 232 if len(a.err) > 0 && !strings.Contains(err.Error(), a.err) { 233 t.Errorf("nodePlugin.Admit() error = %v, expected %v", err, a.err) 234 } 235 }) 236 } 237 238 func Test_nodePlugin_Admit(t *testing.T) { 239 var ( 240 mynode = &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}} 241 bob = &user.DefaultInfo{Name: "bob"} 242 243 mynodeObjMeta = metav1.ObjectMeta{Name: "mynode", UID: "mynode-uid"} 244 mynodeObj = &api.Node{ObjectMeta: mynodeObjMeta} 245 mynodeObjConfigA = &api.Node{ObjectMeta: mynodeObjMeta, Spec: api.NodeSpec{ConfigSource: &api.NodeConfigSource{ 246 ConfigMap: &api.ConfigMapNodeConfigSource{ 247 Name: "foo", 248 Namespace: "bar", 249 UID: "fooUID", 250 KubeletConfigKey: "kubelet", 251 }}}} 252 mynodeObjConfigB = &api.Node{ObjectMeta: mynodeObjMeta, Spec: api.NodeSpec{ConfigSource: &api.NodeConfigSource{ 253 ConfigMap: &api.ConfigMapNodeConfigSource{ 254 Name: "qux", 255 Namespace: "bar", 256 UID: "quxUID", 257 KubeletConfigKey: "kubelet", 258 }}}} 259 260 mynodeObjTaintA = &api.Node{ObjectMeta: mynodeObjMeta, Spec: api.NodeSpec{Taints: []api.Taint{{Key: "mykey", Value: "A"}}}} 261 mynodeObjTaintB = &api.Node{ObjectMeta: mynodeObjMeta, Spec: api.NodeSpec{Taints: []api.Taint{{Key: "mykey", Value: "B"}}}} 262 othernodeObj = &api.Node{ObjectMeta: metav1.ObjectMeta{Name: "othernode"}} 263 264 coremymirrorpod, v1mymirrorpod = makeTestPod("ns", "mymirrorpod", "mynode", true) 265 coreothermirrorpod, v1othermirrorpod = makeTestPod("ns", "othermirrorpod", "othernode", true) 266 coreunboundmirrorpod, v1unboundmirrorpod = makeTestPod("ns", "unboundmirrorpod", "", true) 267 coremypod, v1mypod = makeTestPod("ns", "mypod", "mynode", false) 268 coreotherpod, v1otherpod = makeTestPod("ns", "otherpod", "othernode", false) 269 coreunboundpod, v1unboundpod = makeTestPod("ns", "unboundpod", "", false) 270 coreunnamedpod, _ = makeTestPod("ns", "", "mynode", false) 271 272 mymirrorpodEviction = makeTestPodEviction("mymirrorpod") 273 othermirrorpodEviction = makeTestPodEviction("othermirrorpod") 274 unboundmirrorpodEviction = makeTestPodEviction("unboundmirrorpod") 275 mypodEviction = makeTestPodEviction("mypod") 276 otherpodEviction = makeTestPodEviction("otherpod") 277 unboundpodEviction = makeTestPodEviction("unboundpod") 278 unnamedEviction = makeTestPodEviction("") 279 280 configmapResource = api.Resource("configmap").WithVersion("v1") 281 configmapKind = api.Kind("ConfigMap").WithVersion("v1") 282 283 podResource = api.Resource("pods").WithVersion("v1") 284 podKind = api.Kind("Pod").WithVersion("v1") 285 evictionKind = policy.Kind("Eviction").WithVersion("v1beta1") 286 287 nodeResource = api.Resource("nodes").WithVersion("v1") 288 nodeKind = api.Kind("Node").WithVersion("v1") 289 290 svcacctResource = api.Resource("serviceaccounts").WithVersion("v1") 291 tokenrequestKind = api.Kind("TokenRequest").WithVersion("v1") 292 293 leaseResource = coordination.Resource("leases").WithVersion("v1beta1") 294 leaseKind = coordination.Kind("Lease").WithVersion("v1beta1") 295 lease = &coordination.Lease{ 296 ObjectMeta: metav1.ObjectMeta{ 297 Name: "mynode", 298 Namespace: api.NamespaceNodeLease, 299 }, 300 Spec: coordination.LeaseSpec{ 301 HolderIdentity: pointer.String("mynode"), 302 LeaseDurationSeconds: pointer.Int32(40), 303 RenewTime: &metav1.MicroTime{Time: time.Now()}, 304 }, 305 } 306 leaseWrongNS = &coordination.Lease{ 307 ObjectMeta: metav1.ObjectMeta{ 308 Name: "mynode", 309 Namespace: "foo", 310 }, 311 Spec: coordination.LeaseSpec{ 312 HolderIdentity: pointer.String("mynode"), 313 LeaseDurationSeconds: pointer.Int32(40), 314 RenewTime: &metav1.MicroTime{Time: time.Now()}, 315 }, 316 } 317 leaseWrongName = &coordination.Lease{ 318 ObjectMeta: metav1.ObjectMeta{ 319 Name: "foo", 320 Namespace: api.NamespaceNodeLease, 321 }, 322 Spec: coordination.LeaseSpec{ 323 HolderIdentity: pointer.String("mynode"), 324 LeaseDurationSeconds: pointer.Int32(40), 325 RenewTime: &metav1.MicroTime{Time: time.Now()}, 326 }, 327 } 328 329 csiNodeResource = storage.Resource("csinodes").WithVersion("v1") 330 csiNodeKind = schema.GroupVersionKind{Group: "storage.k8s.io", Version: "v1", Kind: "CSINode"} 331 nodeInfo = &storage.CSINode{ 332 ObjectMeta: metav1.ObjectMeta{ 333 Name: "mynode", 334 }, 335 Spec: storage.CSINodeSpec{ 336 Drivers: []storage.CSINodeDriver{ 337 { 338 Name: "com.example.csi/mydriver", 339 NodeID: "com.example.csi/mynode", 340 TopologyKeys: []string{"com.example.csi/zone"}, 341 }, 342 }, 343 }, 344 } 345 nodeInfoWrongName = &storage.CSINode{ 346 ObjectMeta: metav1.ObjectMeta{ 347 Name: "foo", 348 }, 349 Spec: storage.CSINodeSpec{ 350 Drivers: []storage.CSINodeDriver{ 351 { 352 Name: "com.example.csi/mydriver", 353 NodeID: "com.example.csi/foo", 354 TopologyKeys: []string{"com.example.csi/zone"}, 355 }, 356 }, 357 }, 358 } 359 360 existingNodesIndex = cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil) 361 existingNodes = corev1lister.NewNodeLister(existingNodesIndex) 362 363 noExistingPodsIndex = cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil) 364 noExistingPods = corev1lister.NewPodLister(noExistingPodsIndex) 365 366 existingPodsIndex = cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil) 367 existingPods = corev1lister.NewPodLister(existingPodsIndex) 368 369 labelsA = map[string]string{ 370 "label-a": "value-a", 371 } 372 labelsAB = map[string]string{ 373 "label-a": "value-a", 374 "label-b": "value-b", 375 } 376 aLabeledPod = withLabels(coremypod, labelsA) 377 abLabeledPod = withLabels(coremypod, labelsAB) 378 ) 379 380 existingPodsIndex.Add(v1mymirrorpod) 381 existingPodsIndex.Add(v1othermirrorpod) 382 existingPodsIndex.Add(v1unboundmirrorpod) 383 existingPodsIndex.Add(v1mypod) 384 existingPodsIndex.Add(v1otherpod) 385 existingPodsIndex.Add(v1unboundpod) 386 387 existingNodesIndex.Add(&corev1.Node{ObjectMeta: mynodeObjMeta}) 388 389 sapod, _ := makeTestPod("ns", "mysapod", "mynode", true) 390 sapod.Spec.ServiceAccountName = "foo" 391 392 secretpod, _ := makeTestPod("ns", "mysecretpod", "mynode", true) 393 secretpod.Spec.Volumes = []api.Volume{{VolumeSource: api.VolumeSource{Secret: &api.SecretVolumeSource{SecretName: "foo"}}}} 394 395 configmappod, _ := makeTestPod("ns", "myconfigmappod", "mynode", true) 396 configmappod.Spec.Volumes = []api.Volume{{VolumeSource: api.VolumeSource{ConfigMap: &api.ConfigMapVolumeSource{LocalObjectReference: api.LocalObjectReference{Name: "foo"}}}}} 397 398 ctbpod, _ := makeTestPod("ns", "myctbpod", "mynode", true) 399 ctbpod.Spec.Volumes = []api.Volume{{VolumeSource: api.VolumeSource{Projected: &api.ProjectedVolumeSource{Sources: []api.VolumeProjection{{ClusterTrustBundle: &api.ClusterTrustBundleProjection{Name: pointer.String("foo")}}}}}}} 400 401 pvcpod, _ := makeTestPod("ns", "mypvcpod", "mynode", true) 402 pvcpod.Spec.Volumes = []api.Volume{{VolumeSource: api.VolumeSource{PersistentVolumeClaim: &api.PersistentVolumeClaimVolumeSource{ClaimName: "foo"}}}} 403 404 tests := []admitTestCase{ 405 // Mirror pods bound to us 406 { 407 name: "allow creating a mirror pod bound to self", 408 podsGetter: noExistingPods, 409 attributes: admission.NewAttributesRecord(coremymirrorpod, nil, podKind, coremymirrorpod.Namespace, coremymirrorpod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 410 err: "", 411 }, 412 { 413 name: "forbid update of mirror pod bound to self", 414 podsGetter: existingPods, 415 attributes: admission.NewAttributesRecord(coremymirrorpod, coremymirrorpod, podKind, coremymirrorpod.Namespace, coremymirrorpod.Name, podResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 416 err: "forbidden: unexpected operation", 417 }, 418 { 419 name: "allow delete of mirror pod bound to self", 420 podsGetter: existingPods, 421 attributes: admission.NewAttributesRecord(nil, nil, podKind, coremymirrorpod.Namespace, coremymirrorpod.Name, podResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 422 err: "", 423 }, 424 { 425 name: "forbid create of mirror pod status bound to self", 426 podsGetter: noExistingPods, 427 attributes: admission.NewAttributesRecord(coremymirrorpod, nil, podKind, coremymirrorpod.Namespace, coremymirrorpod.Name, podResource, "status", admission.Create, &metav1.CreateOptions{}, false, mynode), 428 err: "forbidden: unexpected operation", 429 }, 430 { 431 name: "allow update of mirror pod status bound to self", 432 podsGetter: existingPods, 433 attributes: admission.NewAttributesRecord(coremymirrorpod, coremymirrorpod, podKind, coremymirrorpod.Namespace, coremymirrorpod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 434 err: "", 435 }, 436 { 437 name: "forbid delete of mirror pod status bound to self", 438 podsGetter: existingPods, 439 attributes: admission.NewAttributesRecord(nil, nil, podKind, coremymirrorpod.Namespace, coremymirrorpod.Name, podResource, "status", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 440 err: "forbidden: unexpected operation", 441 }, 442 { 443 name: "allow create of eviction for mirror pod bound to self", 444 podsGetter: existingPods, 445 attributes: admission.NewAttributesRecord(mymirrorpodEviction, nil, evictionKind, coremymirrorpod.Namespace, coremymirrorpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 446 err: "", 447 }, 448 { 449 name: "forbid update of eviction for mirror pod bound to self", 450 podsGetter: existingPods, 451 attributes: admission.NewAttributesRecord(mymirrorpodEviction, nil, evictionKind, coremymirrorpod.Namespace, coremymirrorpod.Name, podResource, "eviction", admission.Update, &metav1.UpdateOptions{}, false, mynode), 452 err: "forbidden: unexpected operation", 453 }, 454 { 455 name: "forbid delete of eviction for mirror pod bound to self", 456 podsGetter: existingPods, 457 attributes: admission.NewAttributesRecord(mymirrorpodEviction, nil, evictionKind, coremymirrorpod.Namespace, coremymirrorpod.Name, podResource, "eviction", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 458 err: "forbidden: unexpected operation", 459 }, 460 { 461 name: "allow create of unnamed eviction for mirror pod bound to self", 462 podsGetter: existingPods, 463 attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, coremymirrorpod.Namespace, coremymirrorpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 464 err: "", 465 }, 466 467 // Mirror pods bound to another node 468 { 469 name: "forbid creating a mirror pod bound to another", 470 podsGetter: noExistingPods, 471 attributes: admission.NewAttributesRecord(coreothermirrorpod, nil, podKind, coreothermirrorpod.Namespace, coreothermirrorpod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 472 err: "spec.nodeName set to itself", 473 }, 474 { 475 name: "forbid update of mirror pod bound to another", 476 podsGetter: existingPods, 477 attributes: admission.NewAttributesRecord(coreothermirrorpod, coreothermirrorpod, podKind, coreothermirrorpod.Namespace, coreothermirrorpod.Name, podResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 478 err: "forbidden: unexpected operation", 479 }, 480 { 481 name: "forbid delete of mirror pod bound to another", 482 podsGetter: existingPods, 483 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreothermirrorpod.Namespace, coreothermirrorpod.Name, podResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 484 err: "spec.nodeName set to itself", 485 }, 486 { 487 name: "forbid create of mirror pod status bound to another", 488 podsGetter: noExistingPods, 489 attributes: admission.NewAttributesRecord(coreothermirrorpod, nil, podKind, coreothermirrorpod.Namespace, coreothermirrorpod.Name, podResource, "status", admission.Create, &metav1.CreateOptions{}, false, mynode), 490 err: "forbidden: unexpected operation", 491 }, 492 { 493 name: "forbid update of mirror pod status bound to another", 494 podsGetter: existingPods, 495 attributes: admission.NewAttributesRecord(coreothermirrorpod, coreothermirrorpod, podKind, coreothermirrorpod.Namespace, coreothermirrorpod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 496 err: "spec.nodeName set to itself", 497 }, 498 { 499 name: "forbid delete of mirror pod status bound to another", 500 podsGetter: existingPods, 501 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreothermirrorpod.Namespace, coreothermirrorpod.Name, podResource, "status", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 502 err: "forbidden: unexpected operation", 503 }, 504 { 505 name: "forbid create of eviction for mirror pod bound to another", 506 podsGetter: existingPods, 507 attributes: admission.NewAttributesRecord(othermirrorpodEviction, nil, evictionKind, coreothermirrorpod.Namespace, coreothermirrorpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 508 err: "spec.nodeName set to itself", 509 }, 510 { 511 name: "forbid update of eviction for mirror pod bound to another", 512 podsGetter: existingPods, 513 attributes: admission.NewAttributesRecord(othermirrorpodEviction, nil, evictionKind, coreothermirrorpod.Namespace, coreothermirrorpod.Name, podResource, "eviction", admission.Update, &metav1.UpdateOptions{}, false, mynode), 514 err: "forbidden: unexpected operation", 515 }, 516 { 517 name: "forbid delete of eviction for mirror pod bound to another", 518 podsGetter: existingPods, 519 attributes: admission.NewAttributesRecord(othermirrorpodEviction, nil, evictionKind, coreothermirrorpod.Namespace, coreothermirrorpod.Name, podResource, "eviction", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 520 err: "forbidden: unexpected operation", 521 }, 522 { 523 name: "forbid create of unnamed eviction for mirror pod to another", 524 podsGetter: existingPods, 525 attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, coreothermirrorpod.Namespace, coreothermirrorpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 526 err: "spec.nodeName set to itself", 527 }, 528 529 // Mirror pods not bound to any node 530 { 531 name: "forbid creating a mirror pod unbound", 532 podsGetter: noExistingPods, 533 attributes: admission.NewAttributesRecord(coreunboundmirrorpod, nil, podKind, coreunboundmirrorpod.Namespace, coreunboundmirrorpod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 534 err: "spec.nodeName set to itself", 535 }, 536 { 537 name: "forbid update of mirror pod unbound", 538 podsGetter: existingPods, 539 attributes: admission.NewAttributesRecord(coreunboundmirrorpod, coreunboundmirrorpod, podKind, coreunboundmirrorpod.Namespace, coreunboundmirrorpod.Name, podResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 540 err: "forbidden: unexpected operation", 541 }, 542 { 543 name: "forbid delete of mirror pod unbound", 544 podsGetter: existingPods, 545 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreunboundmirrorpod.Namespace, coreunboundmirrorpod.Name, podResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 546 err: "spec.nodeName set to itself", 547 }, 548 { 549 name: "forbid create of mirror pod status unbound", 550 podsGetter: noExistingPods, 551 attributes: admission.NewAttributesRecord(coreunboundmirrorpod, nil, podKind, coreunboundmirrorpod.Namespace, coreunboundmirrorpod.Name, podResource, "status", admission.Create, &metav1.CreateOptions{}, false, mynode), 552 err: "forbidden: unexpected operation", 553 }, 554 { 555 name: "forbid update of mirror pod status unbound", 556 podsGetter: existingPods, 557 attributes: admission.NewAttributesRecord(coreunboundmirrorpod, coreunboundmirrorpod, podKind, coreunboundmirrorpod.Namespace, coreunboundmirrorpod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 558 err: "spec.nodeName set to itself", 559 }, 560 { 561 name: "forbid delete of mirror pod status unbound", 562 podsGetter: existingPods, 563 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreunboundmirrorpod.Namespace, coreunboundmirrorpod.Name, podResource, "status", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 564 err: "forbidden: unexpected operation", 565 }, 566 { 567 name: "forbid create of eviction for mirror pod unbound", 568 podsGetter: existingPods, 569 attributes: admission.NewAttributesRecord(unboundmirrorpodEviction, nil, evictionKind, coreunboundmirrorpod.Namespace, coreunboundmirrorpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 570 err: "spec.nodeName set to itself", 571 }, 572 { 573 name: "forbid update of eviction for mirror pod unbound", 574 podsGetter: existingPods, 575 attributes: admission.NewAttributesRecord(unboundmirrorpodEviction, nil, evictionKind, coreunboundmirrorpod.Namespace, coreunboundmirrorpod.Name, podResource, "eviction", admission.Update, &metav1.UpdateOptions{}, false, mynode), 576 err: "forbidden: unexpected operation", 577 }, 578 { 579 name: "forbid delete of eviction for mirror pod unbound", 580 podsGetter: existingPods, 581 attributes: admission.NewAttributesRecord(unboundmirrorpodEviction, nil, evictionKind, coreunboundmirrorpod.Namespace, coreunboundmirrorpod.Name, podResource, "eviction", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 582 err: "forbidden: unexpected operation", 583 }, 584 { 585 name: "forbid create of unnamed eviction for mirror pod unbound", 586 podsGetter: existingPods, 587 attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, coreunboundmirrorpod.Namespace, coreunboundmirrorpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 588 err: "spec.nodeName set to itself", 589 }, 590 591 // Normal pods bound to us 592 { 593 name: "forbid creating a normal pod bound to self", 594 podsGetter: noExistingPods, 595 attributes: admission.NewAttributesRecord(coremypod, nil, podKind, coremypod.Namespace, coremypod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 596 err: "can only create mirror pods", 597 }, 598 { 599 name: "forbid update of normal pod bound to self", 600 podsGetter: existingPods, 601 attributes: admission.NewAttributesRecord(coremypod, coremypod, podKind, coremypod.Namespace, coremypod.Name, podResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 602 err: "forbidden: unexpected operation", 603 }, 604 { 605 name: "allow delete of normal pod bound to self", 606 podsGetter: existingPods, 607 attributes: admission.NewAttributesRecord(nil, nil, podKind, coremypod.Namespace, coremypod.Name, podResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 608 err: "", 609 }, 610 { 611 name: "forbid create of normal pod status bound to self", 612 podsGetter: noExistingPods, 613 attributes: admission.NewAttributesRecord(coremypod, nil, podKind, coremypod.Namespace, coremypod.Name, podResource, "status", admission.Create, &metav1.CreateOptions{}, false, mynode), 614 err: "forbidden: unexpected operation", 615 }, 616 { 617 name: "allow update of normal pod status bound to self", 618 podsGetter: existingPods, 619 attributes: admission.NewAttributesRecord(coremypod, coremypod, podKind, coremypod.Namespace, coremypod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 620 err: "", 621 }, 622 { 623 name: "forbid delete of normal pod status bound to self", 624 podsGetter: existingPods, 625 attributes: admission.NewAttributesRecord(nil, nil, podKind, coremypod.Namespace, coremypod.Name, podResource, "status", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 626 err: "forbidden: unexpected operation", 627 }, 628 { 629 name: "forbid addition of pod status preexisting labels", 630 podsGetter: noExistingPods, 631 attributes: admission.NewAttributesRecord(abLabeledPod, aLabeledPod, podKind, coremypod.Namespace, coremypod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 632 err: "cannot update labels through pod status", 633 }, 634 { 635 name: "forbid deletion of pod status preexisting labels", 636 podsGetter: noExistingPods, 637 attributes: admission.NewAttributesRecord(aLabeledPod, abLabeledPod, podKind, coremypod.Namespace, coremypod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 638 err: "cannot update labels through pod status", 639 }, 640 { 641 name: "forbid deletion of all pod status preexisting labels", 642 podsGetter: noExistingPods, 643 attributes: admission.NewAttributesRecord(aLabeledPod, coremypod, podKind, coremypod.Namespace, coremypod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 644 err: "cannot update labels through pod status", 645 }, 646 { 647 name: "forbid addition of pod status labels", 648 podsGetter: noExistingPods, 649 attributes: admission.NewAttributesRecord(coremypod, aLabeledPod, podKind, coremypod.Namespace, coremypod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 650 err: "cannot update labels through pod status", 651 }, 652 { 653 name: "forbid update of eviction for normal pod bound to self", 654 podsGetter: existingPods, 655 attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, coremypod.Namespace, coremypod.Name, podResource, "eviction", admission.Update, &metav1.UpdateOptions{}, false, mynode), 656 err: "forbidden: unexpected operation", 657 }, 658 { 659 name: "forbid delete of eviction for normal pod bound to self", 660 podsGetter: existingPods, 661 attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, coremypod.Namespace, coremypod.Name, podResource, "eviction", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 662 err: "forbidden: unexpected operation", 663 }, 664 { 665 name: "allow create of unnamed eviction for normal pod bound to self", 666 podsGetter: existingPods, 667 attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, coremypod.Namespace, coremypod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 668 err: "", 669 }, 670 671 // Normal pods bound to another 672 { 673 name: "forbid creating a normal pod bound to another", 674 podsGetter: noExistingPods, 675 attributes: admission.NewAttributesRecord(coreotherpod, nil, podKind, coreotherpod.Namespace, coreotherpod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 676 err: "can only create mirror pods", 677 }, 678 { 679 name: "forbid update of normal pod bound to another", 680 podsGetter: existingPods, 681 attributes: admission.NewAttributesRecord(coreotherpod, coreotherpod, podKind, coreotherpod.Namespace, coreotherpod.Name, podResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 682 err: "forbidden: unexpected operation", 683 }, 684 { 685 name: "forbid delete of normal pod bound to another", 686 podsGetter: existingPods, 687 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreotherpod.Namespace, coreotherpod.Name, podResource, "", admission.Delete, &metav1.UpdateOptions{}, false, mynode), 688 err: "spec.nodeName set to itself", 689 }, 690 { 691 name: "forbid create of normal pod status bound to another", 692 podsGetter: noExistingPods, 693 attributes: admission.NewAttributesRecord(coreotherpod, nil, podKind, coreotherpod.Namespace, coreotherpod.Name, podResource, "status", admission.Create, &metav1.CreateOptions{}, false, mynode), 694 err: "forbidden: unexpected operation", 695 }, 696 { 697 name: "forbid update of normal pod status bound to another", 698 podsGetter: existingPods, 699 attributes: admission.NewAttributesRecord(coreotherpod, coreotherpod, podKind, coreotherpod.Namespace, coreotherpod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 700 err: "spec.nodeName set to itself", 701 }, 702 { 703 name: "forbid delete of normal pod status bound to another", 704 podsGetter: existingPods, 705 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreotherpod.Namespace, coreotherpod.Name, podResource, "status", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 706 err: "forbidden: unexpected operation", 707 }, 708 { 709 name: "forbid create of eviction for normal pod bound to another", 710 podsGetter: existingPods, 711 attributes: admission.NewAttributesRecord(otherpodEviction, nil, evictionKind, otherpodEviction.Namespace, otherpodEviction.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 712 err: "spec.nodeName set to itself", 713 }, 714 { 715 name: "forbid update of eviction for normal pod bound to another", 716 podsGetter: existingPods, 717 attributes: admission.NewAttributesRecord(otherpodEviction, nil, evictionKind, otherpodEviction.Namespace, otherpodEviction.Name, podResource, "eviction", admission.Update, &metav1.UpdateOptions{}, false, mynode), 718 err: "forbidden: unexpected operation", 719 }, 720 { 721 name: "forbid delete of eviction for normal pod bound to another", 722 podsGetter: existingPods, 723 attributes: admission.NewAttributesRecord(otherpodEviction, nil, evictionKind, otherpodEviction.Namespace, otherpodEviction.Name, podResource, "eviction", admission.Delete, &metav1.UpdateOptions{}, false, mynode), 724 err: "forbidden: unexpected operation", 725 }, 726 { 727 name: "forbid create of unnamed eviction for normal pod bound to another", 728 podsGetter: existingPods, 729 attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, coreotherpod.Namespace, coreotherpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 730 err: "spec.nodeName set to itself", 731 }, 732 733 // Normal pods not bound to any node 734 { 735 name: "forbid creating a normal pod unbound", 736 podsGetter: noExistingPods, 737 attributes: admission.NewAttributesRecord(coreunboundpod, nil, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 738 err: "can only create mirror pods", 739 }, 740 { 741 name: "forbid update of normal pod unbound", 742 podsGetter: existingPods, 743 attributes: admission.NewAttributesRecord(coreunboundpod, coreunboundpod, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 744 err: "forbidden: unexpected operation", 745 }, 746 { 747 name: "forbid delete of normal pod unbound", 748 podsGetter: existingPods, 749 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 750 err: "spec.nodeName set to itself", 751 }, 752 { 753 name: "forbid create of normal pod status unbound", 754 podsGetter: noExistingPods, 755 attributes: admission.NewAttributesRecord(coreunboundpod, nil, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "status", admission.Create, &metav1.CreateOptions{}, false, mynode), 756 err: "forbidden: unexpected operation", 757 }, 758 { 759 name: "forbid update of normal pod status unbound", 760 podsGetter: existingPods, 761 attributes: admission.NewAttributesRecord(coreunboundpod, coreunboundpod, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 762 err: "spec.nodeName set to itself", 763 }, 764 { 765 name: "forbid delete of normal pod status unbound", 766 podsGetter: existingPods, 767 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "status", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 768 err: "forbidden: unexpected operation", 769 }, 770 { 771 name: "forbid create of eviction for normal pod unbound", 772 podsGetter: existingPods, 773 attributes: admission.NewAttributesRecord(unboundpodEviction, nil, evictionKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 774 err: "spec.nodeName set to itself", 775 }, 776 { 777 name: "forbid update of eviction for normal pod unbound", 778 podsGetter: existingPods, 779 attributes: admission.NewAttributesRecord(unboundpodEviction, nil, evictionKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "eviction", admission.Update, &metav1.UpdateOptions{}, false, mynode), 780 err: "forbidden: unexpected operation", 781 }, 782 { 783 name: "forbid delete of eviction for normal pod unbound", 784 podsGetter: existingPods, 785 attributes: admission.NewAttributesRecord(unboundpodEviction, nil, evictionKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "eviction", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 786 err: "forbidden: unexpected operation", 787 }, 788 { 789 name: "forbid create of unnamed eviction for normal unbound", 790 podsGetter: existingPods, 791 attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 792 err: "spec.nodeName set to itself", 793 }, 794 795 // Missing pod 796 { 797 name: "forbid delete of unknown pod", 798 podsGetter: noExistingPods, 799 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 800 err: "not found", 801 }, 802 { 803 name: "forbid create of eviction for unknown pod", 804 podsGetter: noExistingPods, 805 attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, coremypod.Namespace, coremypod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 806 err: "not found", 807 }, 808 { 809 name: "forbid update of eviction for unknown pod", 810 podsGetter: noExistingPods, 811 attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, coremypod.Namespace, coremypod.Name, podResource, "eviction", admission.Update, &metav1.UpdateOptions{}, false, mynode), 812 err: "forbidden: unexpected operation", 813 }, 814 { 815 name: "forbid delete of eviction for unknown pod", 816 podsGetter: noExistingPods, 817 attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, coremypod.Namespace, coremypod.Name, podResource, "eviction", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 818 err: "forbidden: unexpected operation", 819 }, 820 { 821 name: "forbid create of unnamed eviction for unknown pod", 822 podsGetter: noExistingPods, 823 attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, coremypod.Namespace, coremypod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 824 err: "not found", 825 }, 826 827 // Eviction for unnamed pod 828 { 829 name: "allow create of eviction for unnamed pod", 830 podsGetter: existingPods, 831 attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, coreunnamedpod.Namespace, coreunnamedpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 832 // use the submitted eviction resource name as the pod name 833 err: "", 834 }, 835 { 836 name: "forbid update of eviction for unnamed pod", 837 podsGetter: existingPods, 838 attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, coreunnamedpod.Namespace, coreunnamedpod.Name, podResource, "eviction", admission.Update, &metav1.UpdateOptions{}, false, mynode), 839 err: "forbidden: unexpected operation", 840 }, 841 { 842 name: "forbid delete of eviction for unnamed pod", 843 podsGetter: existingPods, 844 attributes: admission.NewAttributesRecord(mypodEviction, nil, evictionKind, coreunnamedpod.Namespace, coreunnamedpod.Name, podResource, "eviction", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 845 err: "forbidden: unexpected operation", 846 }, 847 { 848 name: "forbid create of unnamed eviction for unnamed pod", 849 podsGetter: existingPods, 850 attributes: admission.NewAttributesRecord(unnamedEviction, nil, evictionKind, coreunnamedpod.Namespace, coreunnamedpod.Name, podResource, "eviction", admission.Create, &metav1.CreateOptions{}, false, mynode), 851 err: "could not determine pod from request data", 852 }, 853 854 // Resource pods 855 { 856 name: "forbid create of pod referencing service account", 857 podsGetter: noExistingPods, 858 attributes: admission.NewAttributesRecord(sapod, nil, podKind, sapod.Namespace, sapod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 859 err: "reference a service account", 860 }, 861 { 862 name: "forbid create of pod referencing secret", 863 podsGetter: noExistingPods, 864 attributes: admission.NewAttributesRecord(secretpod, nil, podKind, secretpod.Namespace, secretpod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 865 err: "reference secrets", 866 }, 867 { 868 name: "forbid create of pod referencing configmap", 869 podsGetter: noExistingPods, 870 attributes: admission.NewAttributesRecord(configmappod, nil, podKind, configmappod.Namespace, configmappod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 871 err: "reference configmaps", 872 }, 873 { 874 name: "forbid create of pod referencing clustertrustbundle", 875 podsGetter: noExistingPods, 876 attributes: admission.NewAttributesRecord(ctbpod, nil, podKind, ctbpod.Namespace, ctbpod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 877 err: "reference clustertrustbundles", 878 }, 879 { 880 name: "forbid create of pod referencing persistentvolumeclaim", 881 podsGetter: noExistingPods, 882 attributes: admission.NewAttributesRecord(pvcpod, nil, podKind, pvcpod.Namespace, pvcpod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 883 err: "reference persistentvolumeclaims", 884 }, 885 886 // My node object 887 { 888 name: "allow create of my node", 889 podsGetter: noExistingPods, 890 attributes: admission.NewAttributesRecord(mynodeObj, nil, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 891 err: "", 892 }, 893 { 894 name: "allow create of my node pulling name from object", 895 podsGetter: noExistingPods, 896 attributes: admission.NewAttributesRecord(mynodeObj, nil, nodeKind, mynodeObj.Namespace, "mynode", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 897 err: "", 898 }, 899 { 900 name: "allow create of my node with taints", 901 podsGetter: noExistingPods, 902 attributes: admission.NewAttributesRecord(mynodeObjTaintA, nil, nodeKind, mynodeObj.Namespace, "mynode", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 903 err: "", 904 }, 905 { 906 name: "allow create of my node with labels", 907 podsGetter: noExistingPods, 908 attributes: admission.NewAttributesRecord(setAllowedCreateLabels(mynodeObj, ""), nil, nodeKind, mynodeObj.Namespace, "mynode", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 909 err: "", 910 }, 911 { 912 name: "forbid create of my node with forbidden labels", 913 podsGetter: noExistingPods, 914 attributes: admission.NewAttributesRecord(setForbiddenCreateLabels(mynodeObj, ""), nil, nodeKind, mynodeObj.Namespace, "mynode", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 915 err: `is not allowed to set the following labels: foo.node-restriction.kubernetes.io/foo, node-restriction.kubernetes.io/foo, other.k8s.io/foo, other.kubernetes.io/foo`, 916 }, 917 { 918 name: "allow update of my node", 919 podsGetter: existingPods, 920 attributes: admission.NewAttributesRecord(mynodeObj, mynodeObj, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 921 err: "", 922 }, 923 { 924 name: "allow delete of my node", 925 podsGetter: existingPods, 926 attributes: admission.NewAttributesRecord(nil, nil, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 927 err: "", 928 }, 929 { 930 name: "allow update of my node status", 931 podsGetter: existingPods, 932 attributes: admission.NewAttributesRecord(mynodeObj, mynodeObj, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 933 err: "", 934 }, 935 { 936 name: "forbid create of my node with non-nil configSource", 937 podsGetter: noExistingPods, 938 attributes: admission.NewAttributesRecord(mynodeObjConfigA, nil, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 939 err: "is not allowed to create pods with a non-nil configSource", 940 }, 941 { 942 name: "forbid update of my node: nil configSource to new non-nil configSource", 943 podsGetter: existingPods, 944 attributes: admission.NewAttributesRecord(mynodeObjConfigA, mynodeObj, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 945 err: "update configSource to a new non-nil configSource", 946 }, 947 { 948 name: "forbid update of my node: non-nil configSource to new non-nil configSource", 949 podsGetter: existingPods, 950 attributes: admission.NewAttributesRecord(mynodeObjConfigB, mynodeObjConfigA, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 951 err: "update configSource to a new non-nil configSource", 952 }, 953 { 954 name: "allow update of my node: non-nil configSource unchanged", 955 podsGetter: existingPods, 956 attributes: admission.NewAttributesRecord(mynodeObjConfigA, mynodeObjConfigA, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 957 err: "", 958 }, 959 { 960 name: "allow update of my node: non-nil configSource to nil configSource", 961 podsGetter: existingPods, 962 attributes: admission.NewAttributesRecord(mynodeObj, mynodeObjConfigA, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 963 err: "", 964 }, 965 { 966 name: "allow update of my node: no change to taints", 967 podsGetter: existingPods, 968 attributes: admission.NewAttributesRecord(mynodeObjTaintA, mynodeObjTaintA, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 969 err: "", 970 }, 971 { 972 name: "allow update of my node: add allowed labels", 973 podsGetter: existingPods, 974 attributes: admission.NewAttributesRecord(setAllowedUpdateLabels(mynodeObj, ""), mynodeObj, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 975 err: "", 976 }, 977 { 978 name: "allow update of my node: remove allowed labels", 979 podsGetter: existingPods, 980 attributes: admission.NewAttributesRecord(mynodeObj, setAllowedUpdateLabels(mynodeObj, ""), nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 981 err: "", 982 }, 983 { 984 name: "allow update of my node: modify allowed labels", 985 podsGetter: existingPods, 986 attributes: admission.NewAttributesRecord(setAllowedUpdateLabels(mynodeObj, "b"), setAllowedUpdateLabels(mynodeObj, "a"), nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 987 err: "", 988 }, 989 { 990 name: "allow update of my node: no change to labels", 991 podsGetter: existingPods, 992 attributes: admission.NewAttributesRecord(setAllLabels(mynodeObj, ""), setAllLabels(mynodeObj, ""), nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 993 err: "", 994 }, 995 { 996 name: "allow update of my node: add allowed labels while forbidden labels exist unmodified", 997 podsGetter: existingPods, 998 attributes: admission.NewAttributesRecord(setAllLabels(mynodeObj, ""), setForbiddenUpdateLabels(mynodeObj, ""), nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 999 err: "", 1000 }, 1001 { 1002 name: "allow update of my node: remove allowed labels while forbidden labels exist unmodified", 1003 podsGetter: existingPods, 1004 attributes: admission.NewAttributesRecord(setForbiddenUpdateLabels(mynodeObj, ""), setAllLabels(mynodeObj, ""), nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1005 err: "", 1006 }, 1007 { 1008 name: "forbid update of my node: add taints", 1009 podsGetter: existingPods, 1010 attributes: admission.NewAttributesRecord(mynodeObjTaintA, mynodeObj, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1011 err: "is not allowed to modify taints", 1012 }, 1013 { 1014 name: "forbid update of my node: remove taints", 1015 podsGetter: existingPods, 1016 attributes: admission.NewAttributesRecord(mynodeObj, mynodeObjTaintA, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1017 err: "is not allowed to modify taints", 1018 }, 1019 { 1020 name: "forbid update of my node: change taints", 1021 podsGetter: existingPods, 1022 attributes: admission.NewAttributesRecord(mynodeObjTaintA, mynodeObjTaintB, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1023 err: "is not allowed to modify taints", 1024 }, 1025 { 1026 name: "forbid update of my node: add labels", 1027 podsGetter: existingPods, 1028 attributes: admission.NewAttributesRecord(setForbiddenUpdateLabels(mynodeObj, ""), mynodeObj, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1029 err: `is not allowed to modify labels: foo.node-restriction.kubernetes.io/foo, node-restriction.kubernetes.io/foo, other.k8s.io/foo, other.kubernetes.io/foo`, 1030 }, 1031 { 1032 name: "forbid update of my node: remove labels", 1033 podsGetter: existingPods, 1034 attributes: admission.NewAttributesRecord(mynodeObj, setForbiddenUpdateLabels(mynodeObj, ""), nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1035 err: `is not allowed to modify labels: foo.node-restriction.kubernetes.io/foo, node-restriction.kubernetes.io/foo, other.k8s.io/foo, other.kubernetes.io/foo`, 1036 }, 1037 { 1038 name: "forbid update of my node: change labels", 1039 podsGetter: existingPods, 1040 attributes: admission.NewAttributesRecord(setForbiddenUpdateLabels(mynodeObj, "new"), setForbiddenUpdateLabels(mynodeObj, "old"), nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1041 err: `is not allowed to modify labels: foo.node-restriction.kubernetes.io/foo, node-restriction.kubernetes.io/foo, other.k8s.io/foo, other.kubernetes.io/foo`, 1042 }, 1043 1044 // Other node object 1045 { 1046 name: "forbid create of other node", 1047 podsGetter: noExistingPods, 1048 attributes: admission.NewAttributesRecord(othernodeObj, nil, nodeKind, othernodeObj.Namespace, othernodeObj.Name, nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 1049 err: "is not allowed to modify node", 1050 }, 1051 { 1052 name: "forbid create of other node pulling name from object", 1053 podsGetter: noExistingPods, 1054 attributes: admission.NewAttributesRecord(othernodeObj, nil, nodeKind, othernodeObj.Namespace, "", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 1055 err: "is not allowed to modify node", 1056 }, 1057 { 1058 name: "forbid update of other node", 1059 podsGetter: existingPods, 1060 attributes: admission.NewAttributesRecord(othernodeObj, othernodeObj, nodeKind, othernodeObj.Namespace, othernodeObj.Name, nodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1061 err: "is not allowed to modify node", 1062 }, 1063 { 1064 name: "forbid delete of other node", 1065 podsGetter: existingPods, 1066 attributes: admission.NewAttributesRecord(nil, nil, nodeKind, othernodeObj.Namespace, othernodeObj.Name, nodeResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 1067 err: "is not allowed to modify node", 1068 }, 1069 { 1070 name: "forbid update of other node status", 1071 podsGetter: existingPods, 1072 attributes: admission.NewAttributesRecord(othernodeObj, othernodeObj, nodeKind, othernodeObj.Namespace, othernodeObj.Name, nodeResource, "status", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1073 err: "is not allowed to modify node", 1074 }, 1075 1076 // Service accounts 1077 { 1078 name: "forbid create of unbound token", 1079 podsGetter: noExistingPods, 1080 attributes: admission.NewAttributesRecord(makeTokenRequest("", ""), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode), 1081 err: "not bound to a pod", 1082 }, 1083 { 1084 name: "forbid create of token bound to nonexistant pod", 1085 podsGetter: noExistingPods, 1086 attributes: admission.NewAttributesRecord(makeTokenRequest("nopod", "someuid"), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode), 1087 err: "not found", 1088 }, 1089 { 1090 name: "forbid create of token bound to pod without uid", 1091 podsGetter: existingPods, 1092 attributes: admission.NewAttributesRecord(makeTokenRequest(coremypod.Name, ""), nil, tokenrequestKind, "ns", "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode), 1093 err: "pod binding without a uid", 1094 }, 1095 { 1096 name: "forbid create of token bound to pod scheduled on another node", 1097 podsGetter: existingPods, 1098 attributes: admission.NewAttributesRecord(makeTokenRequest(coreotherpod.Name, coreotherpod.UID), nil, tokenrequestKind, coreotherpod.Namespace, "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode), 1099 err: "pod scheduled on a different node", 1100 }, 1101 { 1102 name: "allow create of token bound to pod scheduled this node", 1103 podsGetter: existingPods, 1104 attributes: admission.NewAttributesRecord(makeTokenRequest(coremypod.Name, coremypod.UID), nil, tokenrequestKind, coremypod.Namespace, "mysa", svcacctResource, "token", admission.Create, &metav1.CreateOptions{}, false, mynode), 1105 }, 1106 1107 // Unrelated objects 1108 { 1109 name: "allow create of unrelated object", 1110 podsGetter: existingPods, 1111 attributes: admission.NewAttributesRecord(&api.ConfigMap{}, nil, configmapKind, "myns", "mycm", configmapResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 1112 err: "", 1113 }, 1114 { 1115 name: "allow update of unrelated object", 1116 podsGetter: existingPods, 1117 attributes: admission.NewAttributesRecord(&api.ConfigMap{}, &api.ConfigMap{}, configmapKind, "myns", "mycm", configmapResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1118 err: "", 1119 }, 1120 { 1121 name: "allow delete of unrelated object", 1122 podsGetter: existingPods, 1123 attributes: admission.NewAttributesRecord(nil, nil, configmapKind, "myns", "mycm", configmapResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 1124 err: "", 1125 }, 1126 1127 // Unrelated user 1128 { 1129 name: "allow unrelated user creating a normal pod unbound", 1130 podsGetter: noExistingPods, 1131 attributes: admission.NewAttributesRecord(coreunboundpod, nil, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, bob), 1132 err: "", 1133 }, 1134 { 1135 name: "allow unrelated user update of normal pod unbound", 1136 podsGetter: existingPods, 1137 attributes: admission.NewAttributesRecord(coreunboundpod, coreunboundpod, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "", admission.Update, &metav1.UpdateOptions{}, false, bob), 1138 err: "", 1139 }, 1140 { 1141 name: "allow unrelated user delete of normal pod unbound", 1142 podsGetter: existingPods, 1143 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "", admission.Delete, &metav1.DeleteOptions{}, false, bob), 1144 err: "", 1145 }, 1146 { 1147 name: "allow unrelated user create of normal pod status unbound", 1148 podsGetter: noExistingPods, 1149 attributes: admission.NewAttributesRecord(coreunboundpod, nil, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "status", admission.Create, &metav1.CreateOptions{}, false, bob), 1150 err: "", 1151 }, 1152 { 1153 name: "allow unrelated user update of normal pod status unbound", 1154 podsGetter: existingPods, 1155 attributes: admission.NewAttributesRecord(coreunboundpod, coreunboundpod, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "status", admission.Update, &metav1.UpdateOptions{}, false, bob), 1156 err: "", 1157 }, 1158 { 1159 name: "allow unrelated user delete of normal pod status unbound", 1160 podsGetter: existingPods, 1161 attributes: admission.NewAttributesRecord(nil, nil, podKind, coreunboundpod.Namespace, coreunboundpod.Name, podResource, "status", admission.Delete, &metav1.DeleteOptions{}, false, bob), 1162 err: "", 1163 }, 1164 // Node leases 1165 { 1166 name: "disallowed create lease in namespace other than kube-node-lease - feature enabled", 1167 attributes: admission.NewAttributesRecord(leaseWrongNS, nil, leaseKind, leaseWrongNS.Namespace, leaseWrongNS.Name, leaseResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 1168 err: "forbidden: ", 1169 }, 1170 { 1171 name: "disallowed update lease in namespace other than kube-node-lease - feature enabled", 1172 attributes: admission.NewAttributesRecord(leaseWrongNS, leaseWrongNS, leaseKind, leaseWrongNS.Namespace, leaseWrongNS.Name, leaseResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1173 err: "forbidden: ", 1174 }, 1175 { 1176 name: "disallowed delete lease in namespace other than kube-node-lease - feature enabled", 1177 attributes: admission.NewAttributesRecord(nil, nil, leaseKind, leaseWrongNS.Namespace, leaseWrongNS.Name, leaseResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 1178 err: "forbidden: ", 1179 }, 1180 { 1181 name: "disallowed create another node's lease - feature enabled", 1182 attributes: admission.NewAttributesRecord(leaseWrongName, nil, leaseKind, leaseWrongName.Namespace, leaseWrongName.Name, leaseResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 1183 err: "forbidden: ", 1184 }, 1185 { 1186 name: "disallowed update another node's lease - feature enabled", 1187 attributes: admission.NewAttributesRecord(leaseWrongName, leaseWrongName, leaseKind, leaseWrongName.Namespace, leaseWrongName.Name, leaseResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1188 err: "forbidden: ", 1189 }, 1190 { 1191 name: "disallowed delete another node's lease - feature enabled", 1192 attributes: admission.NewAttributesRecord(nil, nil, leaseKind, leaseWrongName.Namespace, leaseWrongName.Name, leaseResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 1193 err: "forbidden: ", 1194 }, 1195 { 1196 name: "allowed create node lease - feature enabled", 1197 attributes: admission.NewAttributesRecord(lease, nil, leaseKind, lease.Namespace, lease.Name, leaseResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 1198 err: "", 1199 }, 1200 { 1201 name: "allowed update node lease - feature enabled", 1202 attributes: admission.NewAttributesRecord(lease, lease, leaseKind, lease.Namespace, lease.Name, leaseResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1203 err: "", 1204 }, 1205 { 1206 name: "allowed delete node lease - feature enabled", 1207 attributes: admission.NewAttributesRecord(nil, nil, leaseKind, lease.Namespace, lease.Name, leaseResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 1208 err: "", 1209 }, 1210 // CSINode 1211 { 1212 name: "disallowed create another node's CSINode", 1213 attributes: admission.NewAttributesRecord(nodeInfoWrongName, nil, csiNodeKind, nodeInfoWrongName.Namespace, nodeInfoWrongName.Name, csiNodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 1214 err: "forbidden: ", 1215 }, 1216 { 1217 name: "disallowed update another node's CSINode", 1218 attributes: admission.NewAttributesRecord(nodeInfoWrongName, nodeInfoWrongName, csiNodeKind, nodeInfoWrongName.Namespace, nodeInfoWrongName.Name, csiNodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1219 err: "forbidden: ", 1220 }, 1221 { 1222 name: "disallowed delete another node's CSINode", 1223 attributes: admission.NewAttributesRecord(nil, nil, csiNodeKind, nodeInfoWrongName.Namespace, nodeInfoWrongName.Name, csiNodeResource, "", admission.Delete, &metav1.DeleteOptions{}, false, mynode), 1224 err: "forbidden: ", 1225 }, 1226 { 1227 name: "allowed create node CSINode", 1228 attributes: admission.NewAttributesRecord(nodeInfo, nil, csiNodeKind, nodeInfo.Namespace, nodeInfo.Name, csiNodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode), 1229 err: "", 1230 }, 1231 { 1232 name: "allowed update node CSINode", 1233 attributes: admission.NewAttributesRecord(nodeInfo, nodeInfo, csiNodeKind, nodeInfo.Namespace, nodeInfo.Name, csiNodeResource, "", admission.Update, &metav1.UpdateOptions{}, false, mynode), 1234 err: "", 1235 }, 1236 { 1237 name: "allowed delete node CSINode", 1238 attributes: admission.NewAttributesRecord(nil, nil, csiNodeKind, nodeInfo.Namespace, nodeInfo.Name, csiNodeResource, "", admission.Delete, &metav1.UpdateOptions{}, false, mynode), 1239 err: "", 1240 }, 1241 } 1242 for _, tt := range tests { 1243 tt.nodesGetter = existingNodes 1244 tt.run(t) 1245 } 1246 } 1247 1248 func Test_nodePlugin_Admit_OwnerReference(t *testing.T) { 1249 expectedNodeIndex := cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil) 1250 expectedNodeIndex.Add(&corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: "mynode", UID: "mynode-uid"}}) 1251 expectedNode := corev1lister.NewNodeLister(expectedNodeIndex) 1252 1253 unexpectedNodeIndex := cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil) 1254 unexpectedNodeIndex.Add(&corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: "mynode", UID: "mynode-unexpected-uid"}}) 1255 unexpectedNode := corev1lister.NewNodeLister(unexpectedNodeIndex) 1256 1257 noNodesIndex := cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil) 1258 noNodes := corev1lister.NewNodeLister(noNodesIndex) 1259 1260 noExistingPodsIndex := cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil) 1261 noExistingPods := corev1lister.NewPodLister(noExistingPodsIndex) 1262 1263 mynode := &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}} 1264 validOwner := metav1.OwnerReference{ 1265 APIVersion: "v1", 1266 Kind: "Node", 1267 Name: "mynode", 1268 UID: "mynode-uid", 1269 Controller: pointer.BoolPtr(true), 1270 } 1271 invalidName := validOwner 1272 invalidName.Name = "other" 1273 invalidKind := validOwner 1274 invalidKind.Kind = "Pod" 1275 invalidAPI := validOwner 1276 invalidAPI.APIVersion = "v2" 1277 invalidControllerNil := validOwner 1278 invalidControllerNil.Controller = nil 1279 invalidControllerFalse := validOwner 1280 invalidControllerFalse.Controller = pointer.BoolPtr(false) 1281 invalidBlockDeletion := validOwner 1282 invalidBlockDeletion.BlockOwnerDeletion = pointer.BoolPtr(true) 1283 1284 tests := []struct { 1285 name string 1286 owners []metav1.OwnerReference 1287 nodesGetter corev1lister.NodeLister 1288 expectErr string 1289 }{ 1290 { 1291 name: "no owner", 1292 owners: nil, 1293 expectErr: "pods \"test\" is forbidden: node \"mynode\" can only create pods with an owner reference set to itself", 1294 }, 1295 { 1296 name: "valid owner", 1297 owners: []metav1.OwnerReference{validOwner}, 1298 }, 1299 { 1300 name: "duplicate owner", 1301 owners: []metav1.OwnerReference{validOwner, validOwner}, 1302 expectErr: "can only create pods with a single owner reference set to itself", 1303 }, 1304 { 1305 name: "invalid name", 1306 owners: []metav1.OwnerReference{invalidName}, 1307 expectErr: "can only create pods with an owner reference set to itself", 1308 }, 1309 { 1310 name: "invalid UID", 1311 owners: []metav1.OwnerReference{validOwner}, 1312 nodesGetter: unexpectedNode, 1313 expectErr: "UID mismatch", 1314 }, 1315 { 1316 name: "node not found", 1317 owners: []metav1.OwnerReference{validOwner}, 1318 nodesGetter: noNodes, 1319 expectErr: "not found", 1320 }, 1321 { 1322 name: "invalid API version", 1323 owners: []metav1.OwnerReference{invalidAPI}, 1324 expectErr: "can only create pods with an owner reference set to itself", 1325 }, 1326 { 1327 name: "invalid kind", 1328 owners: []metav1.OwnerReference{invalidKind}, 1329 expectErr: "can only create pods with an owner reference set to itself", 1330 }, 1331 { 1332 name: "nil controller", 1333 owners: []metav1.OwnerReference{invalidControllerNil}, 1334 expectErr: "can only create pods with a controller owner reference set to itself", 1335 }, 1336 { 1337 name: "false controller", 1338 owners: []metav1.OwnerReference{invalidControllerFalse}, 1339 expectErr: "can only create pods with a controller owner reference set to itself", 1340 }, 1341 { 1342 name: "invalid blockOwnerDeletion", 1343 owners: []metav1.OwnerReference{invalidBlockDeletion}, 1344 expectErr: "must not set blockOwnerDeletion on an owner reference", 1345 }, 1346 } 1347 1348 for _, test := range tests { 1349 if test.nodesGetter == nil { 1350 test.nodesGetter = expectedNode 1351 } 1352 1353 pod, _ := makeTestPod("ns", "test", "mynode", true) 1354 pod.OwnerReferences = test.owners 1355 a := &admitTestCase{ 1356 name: test.name, 1357 podsGetter: noExistingPods, 1358 nodesGetter: test.nodesGetter, 1359 attributes: createPodAttributes(pod, mynode), 1360 err: test.expectErr, 1361 } 1362 a.run(t) 1363 } 1364 } 1365 1366 func Test_getModifiedLabels(t *testing.T) { 1367 tests := []struct { 1368 name string 1369 a map[string]string 1370 b map[string]string 1371 want sets.String 1372 }{ 1373 { 1374 name: "empty", 1375 a: nil, 1376 b: nil, 1377 want: sets.NewString(), 1378 }, 1379 { 1380 name: "no change", 1381 a: map[string]string{"x": "1", "y": "2", "z": "3"}, 1382 b: map[string]string{"x": "1", "y": "2", "z": "3"}, 1383 want: sets.NewString(), 1384 }, 1385 { 1386 name: "added", 1387 a: map[string]string{}, 1388 b: map[string]string{"a": "0"}, 1389 want: sets.NewString("a"), 1390 }, 1391 { 1392 name: "removed", 1393 a: map[string]string{"z": "3"}, 1394 b: map[string]string{}, 1395 want: sets.NewString("z"), 1396 }, 1397 { 1398 name: "changed", 1399 a: map[string]string{"z": "3"}, 1400 b: map[string]string{"z": "4"}, 1401 want: sets.NewString("z"), 1402 }, 1403 { 1404 name: "added empty", 1405 a: map[string]string{}, 1406 b: map[string]string{"a": ""}, 1407 want: sets.NewString("a"), 1408 }, 1409 { 1410 name: "removed empty", 1411 a: map[string]string{"z": ""}, 1412 b: map[string]string{}, 1413 want: sets.NewString("z"), 1414 }, 1415 { 1416 name: "changed to empty", 1417 a: map[string]string{"z": "3"}, 1418 b: map[string]string{"z": ""}, 1419 want: sets.NewString("z"), 1420 }, 1421 { 1422 name: "changed from empty", 1423 a: map[string]string{"z": ""}, 1424 b: map[string]string{"z": "3"}, 1425 want: sets.NewString("z"), 1426 }, 1427 { 1428 name: "added, removed, and changed", 1429 a: map[string]string{"a": "1", "b": "2"}, 1430 b: map[string]string{"a": "2", "c": "3"}, 1431 want: sets.NewString("a", "b", "c"), 1432 }, 1433 } 1434 for _, tt := range tests { 1435 t.Run(tt.name, func(t *testing.T) { 1436 if got := getModifiedLabels(tt.a, tt.b); !reflect.DeepEqual(got, tt.want) { 1437 t.Errorf("getModifiedLabels() = %v, want %v", got, tt.want) 1438 } 1439 }) 1440 } 1441 } 1442 1443 func TestAdmitPVCStatus(t *testing.T) { 1444 expectedNodeIndex := cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil) 1445 expectedNodeIndex.Add(&corev1.Node{ObjectMeta: metav1.ObjectMeta{Name: "mynode", UID: "mynode-uid"}}) 1446 expectedNode := corev1lister.NewNodeLister(expectedNodeIndex) 1447 noExistingPodsIndex := cache.NewIndexer(cache.MetaNamespaceKeyFunc, nil) 1448 noExistingPods := corev1lister.NewPodLister(noExistingPodsIndex) 1449 mynode := &user.DefaultInfo{Name: "system:node:mynode", Groups: []string{"system:nodes"}} 1450 1451 nodeExpansionFailed := api.PersistentVolumeClaimNodeResizeFailed 1452 1453 tests := []struct { 1454 name string 1455 resource schema.GroupVersionResource 1456 subresource string 1457 newObj runtime.Object 1458 oldObj runtime.Object 1459 expansionFeatureEnabled bool 1460 recoveryFeatureEnabled bool 1461 expectError string 1462 }{ 1463 { 1464 name: "should not allow full pvc update from nodes", 1465 oldObj: makeTestPVC( 1466 api.PersistentVolumeClaimResizing, 1467 "10G", nil, 1468 ), 1469 subresource: "", 1470 newObj: makeTestPVC( 1471 "", "10G", nil, 1472 ), 1473 expectError: "is forbidden: may only update PVC status", 1474 }, 1475 { 1476 name: "should allow capacity and condition updates, if expansion is enabled", 1477 oldObj: makeTestPVC( 1478 api.PersistentVolumeClaimResizing, 1479 "10G", nil, 1480 ), 1481 expansionFeatureEnabled: true, 1482 subresource: "status", 1483 newObj: makeTestPVC( 1484 api.PersistentVolumeClaimFileSystemResizePending, 1485 "10G", nil, 1486 ), 1487 expectError: "", 1488 }, 1489 { 1490 name: "should not allow updates to allocatedResources with just expansion enabled", 1491 oldObj: makeTestPVC( 1492 api.PersistentVolumeClaimResizing, 1493 "10G", nil, 1494 ), 1495 subresource: "status", 1496 expansionFeatureEnabled: true, 1497 newObj: makeTestPVC( 1498 api.PersistentVolumeClaimFileSystemResizePending, 1499 "15G", nil, 1500 ), 1501 expectError: "is not allowed to update fields other than", 1502 }, 1503 { 1504 name: "should allow updates to allocatedResources with expansion and recovery enabled", 1505 oldObj: makeTestPVC( 1506 api.PersistentVolumeClaimResizing, 1507 "10G", nil, 1508 ), 1509 subresource: "status", 1510 expansionFeatureEnabled: true, 1511 recoveryFeatureEnabled: true, 1512 newObj: makeTestPVC( 1513 api.PersistentVolumeClaimFileSystemResizePending, 1514 "15G", nil, 1515 ), 1516 expectError: "", 1517 }, 1518 { 1519 name: "should allow updates to resizeStatus with expansion and recovery enabled", 1520 oldObj: makeTestPVC( 1521 api.PersistentVolumeClaimResizing, 1522 "10G", nil, 1523 ), 1524 subresource: "status", 1525 expansionFeatureEnabled: true, 1526 recoveryFeatureEnabled: true, 1527 newObj: makeTestPVC( 1528 api.PersistentVolumeClaimResizing, 1529 "10G", &nodeExpansionFailed, 1530 ), 1531 expectError: "", 1532 }, 1533 } 1534 1535 for i := range tests { 1536 test := tests[i] 1537 t.Run(test.name, func(t *testing.T) { 1538 operation := admission.Update 1539 apiResource := api.SchemeGroupVersion.WithResource("persistentvolumeclaims") 1540 attributes := admission.NewAttributesRecord( 1541 test.newObj, test.oldObj, schema.GroupVersionKind{}, 1542 metav1.NamespaceDefault, "foo", apiResource, test.subresource, operation, &metav1.CreateOptions{}, false, mynode) 1543 featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.RecoverVolumeExpansionFailure, test.recoveryFeatureEnabled) 1544 a := &admitTestCase{ 1545 name: test.name, 1546 podsGetter: noExistingPods, 1547 nodesGetter: expectedNode, 1548 attributes: attributes, 1549 features: feature.DefaultFeatureGate, 1550 err: test.expectError, 1551 } 1552 a.run(t) 1553 }) 1554 1555 } 1556 } 1557 1558 func makeTestPVC( 1559 condition api.PersistentVolumeClaimConditionType, 1560 allocatedResources string, 1561 resizeStatus *api.ClaimResourceStatus) *api.PersistentVolumeClaim { 1562 pvc := &api.PersistentVolumeClaim{ 1563 Spec: api.PersistentVolumeClaimSpec{ 1564 VolumeName: "volume1", 1565 Resources: api.VolumeResourceRequirements{ 1566 Requests: api.ResourceList{ 1567 api.ResourceStorage: resource.MustParse("10G"), 1568 }, 1569 }, 1570 }, 1571 Status: api.PersistentVolumeClaimStatus{ 1572 Capacity: api.ResourceList{ 1573 api.ResourceStorage: resource.MustParse(allocatedResources), 1574 }, 1575 Phase: api.ClaimBound, 1576 AllocatedResources: api.ResourceList{ 1577 api.ResourceStorage: resource.MustParse(allocatedResources), 1578 }, 1579 }, 1580 } 1581 if resizeStatus != nil { 1582 claimStatusMap := map[api.ResourceName]api.ClaimResourceStatus{ 1583 api.ResourceStorage: *resizeStatus, 1584 } 1585 pvc.Status.AllocatedResourceStatuses = claimStatusMap 1586 } 1587 1588 if len(condition) > 0 { 1589 pvc.Status.Conditions = []api.PersistentVolumeClaimCondition{ 1590 { 1591 Type: condition, 1592 Status: api.ConditionTrue, 1593 }, 1594 } 1595 } 1596 1597 return pvc 1598 } 1599 1600 func createPodAttributes(pod *api.Pod, user user.Info) admission.Attributes { 1601 podResource := api.Resource("pods").WithVersion("v1") 1602 podKind := api.Kind("Pod").WithVersion("v1") 1603 return admission.NewAttributesRecord(pod, nil, podKind, pod.Namespace, pod.Name, podResource, "", admission.Create, &metav1.CreateOptions{}, false, user) 1604 } 1605 1606 func TestAdmitResourceSlice(t *testing.T) { 1607 apiResource := resourceapi.SchemeGroupVersion.WithResource("resourceslices") 1608 nodename := "mynode" 1609 mynode := &user.DefaultInfo{Name: "system:node:" + nodename, Groups: []string{"system:nodes"}} 1610 err := "can only create ResourceSlice with the same NodeName as the requesting node" 1611 1612 sliceNode := &resourceapi.ResourceSlice{ 1613 ObjectMeta: metav1.ObjectMeta{ 1614 Name: "something", 1615 }, 1616 NodeName: nodename, 1617 } 1618 sliceOtherNode := &resourceapi.ResourceSlice{ 1619 ObjectMeta: metav1.ObjectMeta{ 1620 Name: "something", 1621 }, 1622 NodeName: nodename + "-other", 1623 } 1624 1625 tests := map[string]struct { 1626 operation admission.Operation 1627 obj runtime.Object 1628 featureEnabled bool 1629 expectError string 1630 }{ 1631 "create allowed, enabled": { 1632 operation: admission.Create, 1633 obj: sliceNode, 1634 featureEnabled: true, 1635 expectError: "", 1636 }, 1637 "create disallowed, enabled": { 1638 operation: admission.Create, 1639 obj: sliceOtherNode, 1640 featureEnabled: true, 1641 expectError: err, 1642 }, 1643 "create allowed, disabled": { 1644 operation: admission.Create, 1645 obj: sliceNode, 1646 featureEnabled: false, 1647 expectError: "", 1648 }, 1649 "create disallowed, disabled": { 1650 operation: admission.Create, 1651 obj: sliceOtherNode, 1652 featureEnabled: false, 1653 expectError: err, 1654 }, 1655 "update allowed, same node": { 1656 operation: admission.Update, 1657 obj: sliceNode, 1658 featureEnabled: true, 1659 expectError: "", 1660 }, 1661 "update allowed, other node": { 1662 operation: admission.Update, 1663 obj: sliceOtherNode, 1664 featureEnabled: true, 1665 expectError: "", 1666 }, 1667 } 1668 1669 for name, test := range tests { 1670 t.Run(name, func(t *testing.T) { 1671 attributes := admission.NewAttributesRecord( 1672 test.obj, nil, schema.GroupVersionKind{}, 1673 "", "foo", apiResource, "", test.operation, &metav1.CreateOptions{}, false, mynode) 1674 featuregatetesting.SetFeatureGateDuringTest(t, feature.DefaultFeatureGate, features.DynamicResourceAllocation, test.featureEnabled) 1675 a := &admitTestCase{ 1676 name: name, 1677 attributes: attributes, 1678 features: feature.DefaultFeatureGate, 1679 err: test.expectError, 1680 } 1681 a.run(t) 1682 }) 1683 1684 } 1685 }