1
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
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
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
143 node.Labels["foo"] = value
144 node.Labels["example.com/foo"] = value
145
146
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
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
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
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
202 node.Labels["node-restriction.kubernetes.io/foo"] = value
203 node.Labels["foo.node-restriction.kubernetes.io/foo"] = value
204
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 defer 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 defer 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 }
1686
View as plain text