...

Source file src/k8s.io/kubernetes/pkg/controller/clusterroleaggregation/clusterroleaggregation_controller_test.go

Documentation: k8s.io/kubernetes/pkg/controller/clusterroleaggregation

     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 clusterroleaggregation
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	rbacv1 "k8s.io/api/rbac/v1"
    25  	"k8s.io/apimachinery/pkg/api/equality"
    26  	"k8s.io/apimachinery/pkg/api/errors"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/apimachinery/pkg/util/yaml"
    30  	rbacv1ac "k8s.io/client-go/applyconfigurations/rbac/v1"
    31  	fakeclient "k8s.io/client-go/kubernetes/fake"
    32  	rbaclisters "k8s.io/client-go/listers/rbac/v1"
    33  	clienttesting "k8s.io/client-go/testing"
    34  	"k8s.io/client-go/tools/cache"
    35  
    36  	"k8s.io/kubernetes/pkg/controller"
    37  )
    38  
    39  func TestSyncClusterRole(t *testing.T) {
    40  	hammerRules := func() []rbacv1.PolicyRule {
    41  		return []rbacv1.PolicyRule{
    42  			{Verbs: []string{"hammer"}, Resources: []string{"nails"}},
    43  			{Verbs: []string{"hammer"}, Resources: []string{"wedges"}},
    44  		}
    45  	}
    46  	chiselRules := func() []rbacv1.PolicyRule {
    47  		return []rbacv1.PolicyRule{
    48  			{Verbs: []string{"chisel"}, Resources: []string{"mortises"}},
    49  		}
    50  	}
    51  	sawRules := func() []rbacv1.PolicyRule {
    52  		return []rbacv1.PolicyRule{
    53  			{Verbs: []string{"saw"}, Resources: []string{"boards"}},
    54  		}
    55  	}
    56  	role := func(name string, labels map[string]string, rules []rbacv1.PolicyRule) *rbacv1.ClusterRole {
    57  		return &rbacv1.ClusterRole{
    58  			ObjectMeta: metav1.ObjectMeta{Name: name, Labels: labels},
    59  			Rules:      rules,
    60  		}
    61  	}
    62  	combinedRole := func(selectors []map[string]string, rules ...[]rbacv1.PolicyRule) *rbacv1.ClusterRole {
    63  		ret := &rbacv1.ClusterRole{
    64  			ObjectMeta:      metav1.ObjectMeta{Name: "combined"},
    65  			AggregationRule: &rbacv1.AggregationRule{},
    66  		}
    67  		for _, selector := range selectors {
    68  			ret.AggregationRule.ClusterRoleSelectors = append(ret.AggregationRule.ClusterRoleSelectors,
    69  				metav1.LabelSelector{MatchLabels: selector})
    70  		}
    71  		for _, currRules := range rules {
    72  			ret.Rules = append(ret.Rules, currRules...)
    73  		}
    74  		return ret
    75  	}
    76  
    77  	combinedApplyRole := func(selectors []map[string]string, rules ...[]rbacv1.PolicyRule) *rbacv1ac.ClusterRoleApplyConfiguration {
    78  		ret := rbacv1ac.ClusterRole("combined")
    79  
    80  		var r []*rbacv1ac.PolicyRuleApplyConfiguration
    81  		for _, currRules := range rules {
    82  			r = append(r, toApplyPolicyRules(currRules)...)
    83  		}
    84  		ret.WithRules(r...)
    85  		return ret
    86  	}
    87  
    88  	tests := []struct {
    89  		name                     string
    90  		startingClusterRoles     []*rbacv1.ClusterRole
    91  		clusterRoleToSync        string
    92  		expectedClusterRole      *rbacv1.ClusterRole
    93  		expectedClusterRoleApply *rbacv1ac.ClusterRoleApplyConfiguration
    94  	}{
    95  		{
    96  			name: "remove dead rules",
    97  			startingClusterRoles: []*rbacv1.ClusterRole{
    98  				role("hammer", map[string]string{"foo": "bar"}, hammerRules()),
    99  				combinedRole([]map[string]string{{"foo": "bar"}}, sawRules()),
   100  			},
   101  			clusterRoleToSync:        "combined",
   102  			expectedClusterRole:      combinedRole([]map[string]string{{"foo": "bar"}}, hammerRules()),
   103  			expectedClusterRoleApply: combinedApplyRole([]map[string]string{{"foo": "bar"}}, hammerRules()),
   104  		},
   105  		{
   106  			name: "strip rules",
   107  			startingClusterRoles: []*rbacv1.ClusterRole{
   108  				role("hammer", map[string]string{"foo": "not-bar"}, hammerRules()),
   109  				combinedRole([]map[string]string{{"foo": "bar"}}, hammerRules()),
   110  			},
   111  			clusterRoleToSync:        "combined",
   112  			expectedClusterRole:      combinedRole([]map[string]string{{"foo": "bar"}}),
   113  			expectedClusterRoleApply: combinedApplyRole([]map[string]string{{"foo": "bar"}}),
   114  		},
   115  		{
   116  			name: "select properly and put in order",
   117  			startingClusterRoles: []*rbacv1.ClusterRole{
   118  				role("hammer", map[string]string{"foo": "bar"}, hammerRules()),
   119  				role("chisel", map[string]string{"foo": "bar"}, chiselRules()),
   120  				role("saw", map[string]string{"foo": "not-bar"}, sawRules()),
   121  				combinedRole([]map[string]string{{"foo": "bar"}}),
   122  			},
   123  			clusterRoleToSync:        "combined",
   124  			expectedClusterRole:      combinedRole([]map[string]string{{"foo": "bar"}}, chiselRules(), hammerRules()),
   125  			expectedClusterRoleApply: combinedApplyRole([]map[string]string{{"foo": "bar"}}, chiselRules(), hammerRules()),
   126  		},
   127  		{
   128  			name: "select properly with multiple selectors",
   129  			startingClusterRoles: []*rbacv1.ClusterRole{
   130  				role("hammer", map[string]string{"foo": "bar"}, hammerRules()),
   131  				role("chisel", map[string]string{"foo": "bar"}, chiselRules()),
   132  				role("saw", map[string]string{"foo": "not-bar"}, sawRules()),
   133  				combinedRole([]map[string]string{{"foo": "bar"}, {"foo": "not-bar"}}),
   134  			},
   135  			clusterRoleToSync:        "combined",
   136  			expectedClusterRole:      combinedRole([]map[string]string{{"foo": "bar"}, {"foo": "not-bar"}}, chiselRules(), hammerRules(), sawRules()),
   137  			expectedClusterRoleApply: combinedApplyRole([]map[string]string{{"foo": "bar"}, {"foo": "not-bar"}}, chiselRules(), hammerRules(), sawRules()),
   138  		},
   139  		{
   140  			name: "select properly remove duplicates",
   141  			startingClusterRoles: []*rbacv1.ClusterRole{
   142  				role("hammer", map[string]string{"foo": "bar"}, hammerRules()),
   143  				role("chisel", map[string]string{"foo": "bar"}, chiselRules()),
   144  				role("saw", map[string]string{"foo": "bar"}, sawRules()),
   145  				role("other-saw", map[string]string{"foo": "not-bar"}, sawRules()),
   146  				combinedRole([]map[string]string{{"foo": "bar"}, {"foo": "not-bar"}}),
   147  			},
   148  			clusterRoleToSync:        "combined",
   149  			expectedClusterRole:      combinedRole([]map[string]string{{"foo": "bar"}, {"foo": "not-bar"}}, chiselRules(), hammerRules(), sawRules()),
   150  			expectedClusterRoleApply: combinedApplyRole([]map[string]string{{"foo": "bar"}, {"foo": "not-bar"}}, chiselRules(), hammerRules(), sawRules()),
   151  		},
   152  		{
   153  			name: "no diff skip",
   154  			startingClusterRoles: []*rbacv1.ClusterRole{
   155  				role("hammer", map[string]string{"foo": "bar"}, hammerRules()),
   156  				combinedRole([]map[string]string{{"foo": "bar"}}, hammerRules()),
   157  			},
   158  			clusterRoleToSync:        "combined",
   159  			expectedClusterRoleApply: nil,
   160  		}}
   161  
   162  	for _, serverSideApplyEnabled := range []bool{true, false} {
   163  		for _, test := range tests {
   164  			t.Run(test.name, func(t *testing.T) {
   165  				indexer := cache.NewIndexer(controller.KeyFunc, cache.Indexers{})
   166  				objs := []runtime.Object{}
   167  				for _, obj := range test.startingClusterRoles {
   168  					objs = append(objs, obj)
   169  					indexer.Add(obj)
   170  				}
   171  				fakeClient := fakeclient.NewSimpleClientset(objs...)
   172  
   173  				// The default reactor doesn't support apply, so we need our own (trivial) reactor
   174  				fakeClient.PrependReactor("patch", "clusterroles", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
   175  					if serverSideApplyEnabled == false {
   176  						// UnsupportedMediaType
   177  						return true, nil, errors.NewGenericServerResponse(415, "get", action.GetResource().GroupResource(), "test", "Apply not supported", 0, true)
   178  					}
   179  					return true, nil, nil // clusterroleaggregator drops returned objects so no point in constructing them
   180  				})
   181  				c := ClusterRoleAggregationController{
   182  					clusterRoleClient: fakeClient.RbacV1(),
   183  					clusterRoleLister: rbaclisters.NewClusterRoleLister(indexer),
   184  				}
   185  				err := c.syncClusterRole(context.TODO(), test.clusterRoleToSync)
   186  				if err != nil {
   187  					t.Fatal(err)
   188  				}
   189  
   190  				if test.expectedClusterRoleApply == nil {
   191  					if len(fakeClient.Actions()) != 0 {
   192  						t.Fatalf("unexpected actions %#v", fakeClient.Actions())
   193  					}
   194  					return
   195  				}
   196  
   197  				expectedActions := 1
   198  				if !serverSideApplyEnabled {
   199  					expectedActions = 2
   200  				}
   201  				if len(fakeClient.Actions()) != expectedActions {
   202  					t.Fatalf("unexpected actions %#v", fakeClient.Actions())
   203  				}
   204  
   205  				action := fakeClient.Actions()[0]
   206  				if !action.Matches("patch", "clusterroles") {
   207  					t.Fatalf("unexpected action %#v", action)
   208  				}
   209  				applyAction, ok := action.(clienttesting.PatchAction)
   210  				if !ok {
   211  					t.Fatalf("unexpected action %#v", action)
   212  				}
   213  				ac := &rbacv1ac.ClusterRoleApplyConfiguration{}
   214  				if err := yaml.Unmarshal(applyAction.GetPatch(), ac); err != nil {
   215  					t.Fatalf("error unmarshalling apply request: %v", err)
   216  				}
   217  				if !equality.Semantic.DeepEqual(ac, test.expectedClusterRoleApply) {
   218  					t.Fatalf("%v", cmp.Diff(test.expectedClusterRoleApply, ac))
   219  				}
   220  				if expectedActions == 2 {
   221  					action := fakeClient.Actions()[1]
   222  					if !action.Matches("update", "clusterroles") {
   223  						t.Fatalf("unexpected action %#v", action)
   224  					}
   225  					updateAction, ok := action.(clienttesting.UpdateAction)
   226  					if !ok {
   227  						t.Fatalf("unexpected action %#v", action)
   228  					}
   229  					if !equality.Semantic.DeepEqual(updateAction.GetObject().(*rbacv1.ClusterRole), test.expectedClusterRole) {
   230  						t.Fatalf("%v", cmp.Diff(test.expectedClusterRole, updateAction.GetObject().(*rbacv1.ClusterRole)))
   231  					}
   232  				}
   233  			})
   234  		}
   235  	}
   236  }
   237  

View as plain text