...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/noderestriction/admission_test.go

Documentation: k8s.io/kubernetes/plugin/pkg/admission/noderestriction

     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  			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