...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/gc/gc_admission_test.go

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

     1  /*
     2  Copyright 2016 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 gc
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  	"testing"
    24  
    25  	appsv1 "k8s.io/api/apps/v1"
    26  	corev1 "k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/api/meta"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	"k8s.io/apiserver/pkg/admission"
    32  	"k8s.io/apiserver/pkg/admission/initializer"
    33  	"k8s.io/apiserver/pkg/authentication/user"
    34  	"k8s.io/apiserver/pkg/authorization/authorizer"
    35  	fakediscovery "k8s.io/client-go/discovery/fake"
    36  	"k8s.io/client-go/restmapper"
    37  	coretesting "k8s.io/client-go/testing"
    38  	api "k8s.io/kubernetes/pkg/apis/core"
    39  	kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
    40  )
    41  
    42  type fakeAuthorizer struct{}
    43  
    44  func (fakeAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
    45  	username := a.GetUser().GetName()
    46  
    47  	if username == "non-deleter" {
    48  		if a.GetVerb() == "delete" {
    49  			return authorizer.DecisionNoOpinion, "", nil
    50  		}
    51  		if a.GetVerb() == "update" && a.GetSubresource() == "finalizers" {
    52  			return authorizer.DecisionNoOpinion, "", nil
    53  		}
    54  		if a.GetAPIGroup() == "*" && a.GetResource() == "*" { // this user does not have full rights
    55  			return authorizer.DecisionNoOpinion, "", nil
    56  		}
    57  		return authorizer.DecisionAllow, "", nil
    58  	}
    59  
    60  	if username == "non-pod-deleter" {
    61  		if a.GetVerb() == "delete" && a.GetResource() == "pods" {
    62  			return authorizer.DecisionNoOpinion, "", nil
    63  		}
    64  		if a.GetVerb() == "update" && a.GetResource() == "pods" && a.GetSubresource() == "finalizers" {
    65  			return authorizer.DecisionNoOpinion, "", nil
    66  		}
    67  		if a.GetAPIGroup() == "*" && a.GetResource() == "*" { // this user does not have full rights
    68  			return authorizer.DecisionNoOpinion, "", nil
    69  		}
    70  		return authorizer.DecisionAllow, "", nil
    71  	}
    72  
    73  	if username == "non-rc-deleter" {
    74  		if a.GetVerb() == "delete" && a.GetResource() == "replicationcontrollers" {
    75  			return authorizer.DecisionNoOpinion, "", nil
    76  		}
    77  		if a.GetVerb() == "update" && a.GetResource() == "replicationcontrollers" && a.GetSubresource() == "finalizers" {
    78  			return authorizer.DecisionNoOpinion, "", nil
    79  		}
    80  		if a.GetAPIGroup() == "*" && a.GetResource() == "*" { // this user does not have full rights
    81  			return authorizer.DecisionNoOpinion, "", nil
    82  		}
    83  		return authorizer.DecisionAllow, "", nil
    84  	}
    85  
    86  	if username == "non-node-deleter" {
    87  		if a.GetVerb() == "delete" && a.GetResource() == "nodes" {
    88  			return authorizer.DecisionNoOpinion, "", nil
    89  		}
    90  		if a.GetVerb() == "update" && a.GetResource() == "nodes" && a.GetSubresource() == "finalizers" {
    91  			return authorizer.DecisionNoOpinion, "", nil
    92  		}
    93  		if a.GetAPIGroup() == "*" && a.GetResource() == "*" { // this user does not have full rights
    94  			return authorizer.DecisionNoOpinion, "", nil
    95  		}
    96  		return authorizer.DecisionAllow, "", nil
    97  	}
    98  
    99  	return authorizer.DecisionAllow, "", nil
   100  }
   101  
   102  // newGCPermissionsEnforcement returns the admission controller configured for testing.
   103  func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) {
   104  	// the pods/status endpoint is ignored by this plugin since old kubelets
   105  	// corrupt them.  the pod status strategy ensures status updates cannot mutate
   106  	// ownerRef.
   107  	whiteList := []whiteListItem{
   108  		{
   109  			groupResource: schema.GroupResource{Resource: "pods"},
   110  			subresource:   "status",
   111  		},
   112  	}
   113  	gcAdmit := &gcPermissionsEnforcement{
   114  		Handler:   admission.NewHandler(admission.Create, admission.Update),
   115  		whiteList: whiteList,
   116  	}
   117  
   118  	fakeDiscoveryClient := &fakediscovery.FakeDiscovery{Fake: &coretesting.Fake{}}
   119  	fakeDiscoveryClient.Resources = []*metav1.APIResourceList{
   120  		{
   121  			GroupVersion: corev1.SchemeGroupVersion.String(),
   122  			APIResources: []metav1.APIResource{
   123  				{Name: "nodes", Namespaced: false, Kind: "Node"},
   124  				{Name: "pods", Namespaced: true, Kind: "Pod"},
   125  				{Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
   126  			},
   127  		},
   128  		{
   129  			GroupVersion: appsv1.SchemeGroupVersion.String(),
   130  			APIResources: []metav1.APIResource{
   131  				{Name: "daemonsets", Namespaced: true, Kind: "DaemonSet"},
   132  			},
   133  		},
   134  	}
   135  	restMapperRes, err := restmapper.GetAPIGroupResources(fakeDiscoveryClient)
   136  	if err != nil {
   137  		return nil, fmt.Errorf("unexpected error while constructing resource list from fake discovery client: %v", err)
   138  	}
   139  	restMapper := restmapper.NewDiscoveryRESTMapper(restMapperRes)
   140  	genericPluginInitializer := initializer.New(nil, nil, nil, fakeAuthorizer{}, nil, nil, restMapper)
   141  
   142  	pluginInitializer := kubeadmission.NewPluginInitializer(nil, nil, nil)
   143  	initializersChain := admission.PluginInitializers{}
   144  	initializersChain = append(initializersChain, genericPluginInitializer)
   145  	initializersChain = append(initializersChain, pluginInitializer)
   146  
   147  	initializersChain.Initialize(gcAdmit)
   148  	return gcAdmit, nil
   149  }
   150  
   151  type neverReturningRESTMapper struct{}
   152  
   153  var _ meta.RESTMapper = &neverReturningRESTMapper{}
   154  
   155  func (r *neverReturningRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
   156  	// this ok because if the test works, this method should never be called.
   157  	panic("test failed")
   158  }
   159  func (r *neverReturningRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
   160  	// this ok because if the test works, this method should never be called.
   161  	panic("test failed")
   162  }
   163  func (r *neverReturningRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
   164  	// this ok because if the test works, this method should never be called.
   165  	panic("test failed")
   166  }
   167  func (r *neverReturningRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
   168  	// this ok because if the test works, this method should never be called.
   169  	panic("test failed")
   170  }
   171  func (r *neverReturningRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
   172  	// this ok because if the test works, this method should never be called.
   173  	panic("test failed")
   174  }
   175  func (r *neverReturningRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
   176  	// this ok because if the test works, this method should never be called.
   177  	panic("test failed")
   178  }
   179  func (r *neverReturningRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
   180  	// this ok because if the test works, this method should never be called.
   181  	panic("test failed")
   182  }
   183  
   184  func TestGCAdmission(t *testing.T) {
   185  	expectNoError := func(err error) bool {
   186  		return err == nil
   187  	}
   188  	expectCantSetOwnerRefError := func(err error) bool {
   189  		if err == nil {
   190  			return false
   191  		}
   192  		return strings.Contains(err.Error(), "cannot set an ownerRef on a resource you can't delete")
   193  	}
   194  	tests := []struct {
   195  		name        string
   196  		username    string
   197  		resource    schema.GroupVersionResource
   198  		subresource string
   199  		oldObj      runtime.Object
   200  		newObj      runtime.Object
   201  
   202  		checkError func(error) bool
   203  	}{
   204  		{
   205  			name:       "super-user, create, no objectref change",
   206  			username:   "super",
   207  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   208  			newObj:     &api.Pod{},
   209  			checkError: expectNoError,
   210  		},
   211  		{
   212  			name:       "super-user, create, objectref change",
   213  			username:   "super",
   214  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   215  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   216  			checkError: expectNoError,
   217  		},
   218  		{
   219  			name:       "non-deleter, create, no objectref change",
   220  			username:   "non-deleter",
   221  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   222  			newObj:     &api.Pod{},
   223  			checkError: expectNoError,
   224  		},
   225  		{
   226  			name:       "non-deleter, create, objectref change",
   227  			username:   "non-deleter",
   228  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   229  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   230  			checkError: expectNoError,
   231  		},
   232  		{
   233  			name:       "non-pod-deleter, create, no objectref change",
   234  			username:   "non-pod-deleter",
   235  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   236  			newObj:     &api.Pod{},
   237  			checkError: expectNoError,
   238  		},
   239  		{
   240  			name:       "non-pod-deleter, create, objectref change",
   241  			username:   "non-pod-deleter",
   242  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   243  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   244  			checkError: expectNoError,
   245  		},
   246  		{
   247  			name:       "non-pod-deleter, create, objectref change, but not a pod",
   248  			username:   "non-pod-deleter",
   249  			resource:   api.SchemeGroupVersion.WithResource("not-pods"),
   250  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   251  			checkError: expectNoError,
   252  		},
   253  
   254  		{
   255  			name:       "super-user, update, no objectref change",
   256  			username:   "super",
   257  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   258  			oldObj:     &api.Pod{},
   259  			newObj:     &api.Pod{},
   260  			checkError: expectNoError,
   261  		},
   262  		{
   263  			name:       "super-user, update, no objectref change two",
   264  			username:   "super",
   265  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   266  			oldObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   267  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   268  			checkError: expectNoError,
   269  		},
   270  		{
   271  			name:       "super-user, update, objectref change",
   272  			username:   "super",
   273  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   274  			oldObj:     &api.Pod{},
   275  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   276  			checkError: expectNoError,
   277  		},
   278  		{
   279  			name:       "non-deleter, update, no objectref change",
   280  			username:   "non-deleter",
   281  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   282  			oldObj:     &api.Pod{},
   283  			newObj:     &api.Pod{},
   284  			checkError: expectNoError,
   285  		},
   286  		{
   287  			name:       "non-deleter, update, no objectref change two",
   288  			username:   "non-deleter",
   289  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   290  			oldObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   291  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   292  			checkError: expectNoError,
   293  		},
   294  		{
   295  			name:       "non-deleter, update, objectref change",
   296  			username:   "non-deleter",
   297  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   298  			oldObj:     &api.Pod{},
   299  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   300  			checkError: expectCantSetOwnerRefError,
   301  		},
   302  		{
   303  			name:       "non-deleter, update, objectref change two",
   304  			username:   "non-deleter",
   305  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   306  			oldObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   307  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}, {Name: "second"}}}},
   308  			checkError: expectCantSetOwnerRefError,
   309  		},
   310  		{
   311  			name:       "non-pod-deleter, update, no objectref change",
   312  			username:   "non-pod-deleter",
   313  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   314  			oldObj:     &api.Pod{},
   315  			newObj:     &api.Pod{},
   316  			checkError: expectNoError,
   317  		},
   318  		{
   319  			name:        "non-pod-deleter, update status, objectref change",
   320  			username:    "non-pod-deleter",
   321  			resource:    api.SchemeGroupVersion.WithResource("pods"),
   322  			subresource: "status",
   323  			oldObj:      &api.Pod{},
   324  			newObj:      &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   325  			checkError:  expectNoError,
   326  		},
   327  		{
   328  			name:       "non-pod-deleter, update, objectref change",
   329  			username:   "non-pod-deleter",
   330  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   331  			oldObj:     &api.Pod{},
   332  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   333  			checkError: expectCantSetOwnerRefError,
   334  		},
   335  		{
   336  			name:       "non-pod-deleter, update, objectref change, but not a pod",
   337  			username:   "non-pod-deleter",
   338  			resource:   api.SchemeGroupVersion.WithResource("not-pods"),
   339  			oldObj:     &api.Pod{},
   340  			newObj:     &api.Pod{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{{Name: "first"}}}},
   341  			checkError: expectNoError,
   342  		},
   343  	}
   344  
   345  	for _, tc := range tests {
   346  		t.Run(tc.name, func(t *testing.T) {
   347  			gcAdmit, err := newGCPermissionsEnforcement()
   348  			if err != nil {
   349  				t.Error(err)
   350  			}
   351  
   352  			operation := admission.Create
   353  			var options runtime.Object = &metav1.CreateOptions{}
   354  			if tc.oldObj != nil {
   355  				operation = admission.Update
   356  				options = &metav1.UpdateOptions{}
   357  			}
   358  			user := &user.DefaultInfo{Name: tc.username}
   359  			attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, options, false, user)
   360  
   361  			err = gcAdmit.Validate(context.TODO(), attributes, nil)
   362  			if !tc.checkError(err) {
   363  				t.Errorf("unexpected err: %v", err)
   364  			}
   365  		})
   366  	}
   367  }
   368  
   369  func TestBlockOwnerDeletionAdmission(t *testing.T) {
   370  	podWithOwnerRefs := func(refs ...metav1.OwnerReference) *api.Pod {
   371  		var refSlice []metav1.OwnerReference
   372  		refSlice = append(refSlice, refs...)
   373  
   374  		return &api.Pod{
   375  			ObjectMeta: metav1.ObjectMeta{
   376  				OwnerReferences: refSlice,
   377  			},
   378  		}
   379  	}
   380  
   381  	getTrueVar := func() *bool {
   382  		ret := true
   383  		return &ret
   384  	}
   385  
   386  	getFalseVar := func() *bool {
   387  		ret := false
   388  		return &ret
   389  	}
   390  	blockRC1 := metav1.OwnerReference{
   391  		APIVersion:         "v1",
   392  		Kind:               "ReplicationController",
   393  		Name:               "rc1",
   394  		BlockOwnerDeletion: getTrueVar(),
   395  	}
   396  	blockRC2 := metav1.OwnerReference{
   397  		APIVersion:         "v1",
   398  		Kind:               "ReplicationController",
   399  		Name:               "rc2",
   400  		BlockOwnerDeletion: getTrueVar(),
   401  	}
   402  	notBlockRC1 := metav1.OwnerReference{
   403  		APIVersion:         "v1",
   404  		Kind:               "ReplicationController",
   405  		Name:               "rc1",
   406  		BlockOwnerDeletion: getFalseVar(),
   407  	}
   408  	notBlockRC2 := metav1.OwnerReference{
   409  		APIVersion:         "v1",
   410  		Kind:               "ReplicationController",
   411  		Name:               "rc2",
   412  		BlockOwnerDeletion: getFalseVar(),
   413  	}
   414  	nilBlockRC1 := metav1.OwnerReference{
   415  		APIVersion: "v1",
   416  		Kind:       "ReplicationController",
   417  		Name:       "rc1",
   418  	}
   419  	nilBlockRC2 := metav1.OwnerReference{
   420  		APIVersion: "v1",
   421  		Kind:       "ReplicationController",
   422  		Name:       "rc2",
   423  	}
   424  	blockDS1 := metav1.OwnerReference{
   425  		APIVersion:         "apps/v1",
   426  		Kind:               "DaemonSet",
   427  		Name:               "ds1",
   428  		BlockOwnerDeletion: getTrueVar(),
   429  	}
   430  	notBlockDS1 := metav1.OwnerReference{
   431  		APIVersion:         "apps/v1",
   432  		Kind:               "DaemonSet",
   433  		Name:               "ds1",
   434  		BlockOwnerDeletion: getFalseVar(),
   435  	}
   436  	blockNode := metav1.OwnerReference{
   437  		APIVersion:         "v1",
   438  		Kind:               "Node",
   439  		Name:               "node1",
   440  		BlockOwnerDeletion: getTrueVar(),
   441  	}
   442  	notBlockNode := metav1.OwnerReference{
   443  		APIVersion:         "v1",
   444  		Kind:               "Node",
   445  		Name:               "node",
   446  		BlockOwnerDeletion: getFalseVar(),
   447  	}
   448  	nilBlockNode := metav1.OwnerReference{
   449  		APIVersion: "v1",
   450  		Kind:       "Node",
   451  		Name:       "node",
   452  	}
   453  
   454  	expectNoError := func(err error) bool {
   455  		return err == nil
   456  	}
   457  	expectCantSetBlockOwnerDeletionError := func(err error) bool {
   458  		if err == nil {
   459  			return false
   460  		}
   461  		return strings.Contains(err.Error(), "cannot set blockOwnerDeletion if an ownerReference refers to a resource you can't set finalizers on")
   462  	}
   463  	tests := []struct {
   464  		name               string
   465  		username           string
   466  		resource           schema.GroupVersionResource
   467  		subresource        string
   468  		oldObj             runtime.Object
   469  		newObj             runtime.Object
   470  		restMapperOverride meta.RESTMapper
   471  
   472  		checkError func(error) bool
   473  	}{
   474  		// cases for create
   475  		{
   476  			name:       "super-user, create, no ownerReferences",
   477  			username:   "super",
   478  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   479  			newObj:     podWithOwnerRefs(),
   480  			checkError: expectNoError,
   481  		},
   482  		{
   483  			name:       "super-user, create, all ownerReferences have blockOwnerDeletion=false",
   484  			username:   "super",
   485  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   486  			newObj:     podWithOwnerRefs(notBlockRC1, notBlockRC2),
   487  			checkError: expectNoError,
   488  		},
   489  		{
   490  			name:       "super-user, create, some ownerReferences have blockOwnerDeletion=true",
   491  			username:   "super",
   492  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   493  			newObj:     podWithOwnerRefs(blockRC1, blockRC2, blockNode),
   494  			checkError: expectNoError,
   495  		},
   496  		{
   497  			name:               "super-user, create, some ownerReferences have blockOwnerDeletion=true, hangingRESTMapper",
   498  			username:           "super",
   499  			resource:           api.SchemeGroupVersion.WithResource("pods"),
   500  			newObj:             podWithOwnerRefs(blockRC1, blockRC2, blockNode),
   501  			restMapperOverride: &neverReturningRESTMapper{},
   502  			checkError:         expectNoError,
   503  		},
   504  		{
   505  			name:       "non-rc-deleter, create, no ownerReferences",
   506  			username:   "non-rc-deleter",
   507  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   508  			newObj:     podWithOwnerRefs(),
   509  			checkError: expectNoError,
   510  		},
   511  		{
   512  			name:       "non-rc-deleter, create, all ownerReferences have blockOwnerDeletion=false or nil",
   513  			username:   "non-rc-deleter",
   514  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   515  			newObj:     podWithOwnerRefs(notBlockRC1, nilBlockRC2),
   516  			checkError: expectNoError,
   517  		},
   518  		{
   519  			name:       "non-node-deleter, create, all ownerReferences have blockOwnerDeletion=false",
   520  			username:   "non-node-deleter",
   521  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   522  			newObj:     podWithOwnerRefs(notBlockNode),
   523  			checkError: expectNoError,
   524  		},
   525  		{
   526  			name:       "non-rc-deleter, create, some ownerReferences have blockOwnerDeletion=true",
   527  			username:   "non-rc-deleter",
   528  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   529  			newObj:     podWithOwnerRefs(blockRC1, notBlockRC2),
   530  			checkError: expectCantSetBlockOwnerDeletionError,
   531  		},
   532  		{
   533  			name:       "non-rc-deleter, create, some ownerReferences have blockOwnerDeletion=true, but are pointing to daemonset",
   534  			username:   "non-rc-deleter",
   535  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   536  			newObj:     podWithOwnerRefs(blockDS1),
   537  			checkError: expectNoError,
   538  		},
   539  		{
   540  			name:       "non-node-deleter, create, some ownerReferences have blockOwnerDeletion=true",
   541  			username:   "non-node-deleter",
   542  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   543  			newObj:     podWithOwnerRefs(blockNode),
   544  			checkError: expectCantSetBlockOwnerDeletionError,
   545  		},
   546  		// cases are for update
   547  		{
   548  			name:       "super-user, update, no ownerReferences change blockOwnerDeletion",
   549  			username:   "super",
   550  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   551  			oldObj:     podWithOwnerRefs(nilBlockRC1, nilBlockNode),
   552  			newObj:     podWithOwnerRefs(notBlockRC1, notBlockNode),
   553  			checkError: expectNoError,
   554  		},
   555  		{
   556  			name:       "super-user, update, some ownerReferences change to blockOwnerDeletion=true",
   557  			username:   "super",
   558  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   559  			oldObj:     podWithOwnerRefs(notBlockRC1, notBlockNode),
   560  			newObj:     podWithOwnerRefs(blockRC1, blockNode),
   561  			checkError: expectNoError,
   562  		},
   563  		{
   564  			name:       "super-user, update, add new ownerReferences with blockOwnerDeletion=true",
   565  			username:   "super",
   566  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   567  			oldObj:     podWithOwnerRefs(),
   568  			newObj:     podWithOwnerRefs(blockRC1, blockNode),
   569  			checkError: expectNoError,
   570  		},
   571  		{
   572  			name:       "non-rc-deleter, update, no ownerReferences change blockOwnerDeletion",
   573  			username:   "non-rc-deleter",
   574  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   575  			oldObj:     podWithOwnerRefs(nilBlockRC1),
   576  			newObj:     podWithOwnerRefs(notBlockRC1),
   577  			checkError: expectNoError,
   578  		},
   579  		{
   580  			name:       "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=false to true",
   581  			username:   "non-rc-deleter",
   582  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   583  			oldObj:     podWithOwnerRefs(notBlockRC1),
   584  			newObj:     podWithOwnerRefs(blockRC1),
   585  			checkError: expectCantSetBlockOwnerDeletionError,
   586  		},
   587  		{
   588  			name:       "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=nil to true",
   589  			username:   "non-rc-deleter",
   590  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   591  			oldObj:     podWithOwnerRefs(nilBlockRC1),
   592  			newObj:     podWithOwnerRefs(blockRC1),
   593  			checkError: expectCantSetBlockOwnerDeletionError,
   594  		},
   595  		{
   596  			name:       "non-node-deleter, update, some ownerReferences change from blockOwnerDeletion=nil to true",
   597  			username:   "non-node-deleter",
   598  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   599  			oldObj:     podWithOwnerRefs(nilBlockNode),
   600  			newObj:     podWithOwnerRefs(blockNode),
   601  			checkError: expectCantSetBlockOwnerDeletionError,
   602  		},
   603  		{
   604  			name:       "non-rc-deleter, update, some ownerReferences change from blockOwnerDeletion=true to false",
   605  			username:   "non-rc-deleter",
   606  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   607  			oldObj:     podWithOwnerRefs(blockRC1),
   608  			newObj:     podWithOwnerRefs(notBlockRC1),
   609  			checkError: expectNoError,
   610  		},
   611  		{
   612  			name:       "non-node-deleter, update, some ownerReferences change from blockOwnerDeletion=true to false",
   613  			username:   "non-node-deleter",
   614  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   615  			oldObj:     podWithOwnerRefs(blockNode),
   616  			newObj:     podWithOwnerRefs(notBlockNode),
   617  			checkError: expectNoError,
   618  		},
   619  		{
   620  			name:       "non-rc-deleter, update, some ownerReferences change blockOwnerDeletion, but all such references are to daemonset",
   621  			username:   "non-rc-deleter",
   622  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   623  			oldObj:     podWithOwnerRefs(notBlockDS1),
   624  			newObj:     podWithOwnerRefs(blockDS1),
   625  			checkError: expectNoError,
   626  		},
   627  		{
   628  			name:       "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=nil or false",
   629  			username:   "non-rc-deleter",
   630  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   631  			oldObj:     podWithOwnerRefs(),
   632  			newObj:     podWithOwnerRefs(notBlockRC1, nilBlockRC2),
   633  			checkError: expectNoError,
   634  		},
   635  		{
   636  			name:       "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=true",
   637  			username:   "non-rc-deleter",
   638  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   639  			oldObj:     podWithOwnerRefs(),
   640  			newObj:     podWithOwnerRefs(blockRC1),
   641  			checkError: expectCantSetBlockOwnerDeletionError,
   642  		},
   643  		{
   644  			name:       "non-rc-deleter, update, add new ownerReferences with blockOwnerDeletion=true, but the references are to daemonset",
   645  			username:   "non-rc-deleter",
   646  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   647  			oldObj:     podWithOwnerRefs(),
   648  			newObj:     podWithOwnerRefs(blockDS1),
   649  			checkError: expectNoError,
   650  		},
   651  		{
   652  			name:       "non-node-deleter, update, add ownerReferences with blockOwnerDeletion=true",
   653  			username:   "non-node-deleter",
   654  			resource:   api.SchemeGroupVersion.WithResource("pods"),
   655  			oldObj:     podWithOwnerRefs(),
   656  			newObj:     podWithOwnerRefs(blockNode),
   657  			checkError: expectCantSetBlockOwnerDeletionError,
   658  		},
   659  	}
   660  
   661  	for _, tc := range tests {
   662  		t.Run(tc.name, func(t *testing.T) {
   663  			gcAdmit, err := newGCPermissionsEnforcement()
   664  			if err != nil {
   665  				t.Fatal(err)
   666  			}
   667  			if tc.restMapperOverride != nil {
   668  				gcAdmit.restMapper = tc.restMapperOverride
   669  			}
   670  
   671  			operation := admission.Create
   672  			var options runtime.Object = &metav1.CreateOptions{}
   673  			if tc.oldObj != nil {
   674  				operation = admission.Update
   675  				options = &metav1.UpdateOptions{}
   676  			}
   677  			user := &user.DefaultInfo{Name: tc.username}
   678  			attributes := admission.NewAttributesRecord(tc.newObj, tc.oldObj, schema.GroupVersionKind{}, metav1.NamespaceDefault, "foo", tc.resource, tc.subresource, operation, options, false, user)
   679  
   680  			err = gcAdmit.Validate(context.TODO(), attributes, nil)
   681  			if !tc.checkError(err) {
   682  				t.Fatal(err)
   683  			}
   684  		})
   685  	}
   686  }
   687  

View as plain text