1
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
174 fakeClient.PrependReactor("patch", "clusterroles", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
175 if serverSideApplyEnabled == false {
176
177 return true, nil, errors.NewGenericServerResponse(415, "get", action.GetResource().GroupResource(), "test", "Apply not supported", 0, true)
178 }
179 return true, nil, nil
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