Skip to content

Commit c5450ae

Browse files
feat: implement incremental RBAC rule application and add stress testing scripts (#181)
1 parent 424e718 commit c5450ae

3 files changed

Lines changed: 186 additions & 41 deletions

File tree

internal/tools/rbac/installer.go

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66

77
"k8s.io/apimachinery/pkg/api/errors"
88

9+
"reflect"
10+
911
corev1 "k8s.io/api/core/v1"
1012
rbacv1 "k8s.io/api/rbac/v1"
1113
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -94,6 +96,8 @@ func (r *RBACInstaller) ApplyNamespace(ctx context.Context, namespace *corev1.Na
9496
return nil, fmt.Errorf("failed to convert unstructured to Namespace: %w", err)
9597
}
9698
return namespace, nil
99+
} else if err != nil {
100+
return nil, fmt.Errorf("failed to Get Namespace: %w", err)
97101
}
98102

99103
res, err := cli.Update(ctx, u, metav1.UpdateOptions{})
@@ -123,7 +127,8 @@ func (i *RBACInstaller) ApplyRole(ctx context.Context, role *rbacv1.Role) (*rbac
123127
},
124128
).Namespace(role.Namespace)
125129

126-
if _, err = cli.Get(ctx, role.Name, metav1.GetOptions{}); errors.IsNotFound(err) {
130+
existingU, err := cli.Get(ctx, role.Name, metav1.GetOptions{})
131+
if errors.IsNotFound(err) {
127132
res, err := cli.Create(ctx, u, metav1.CreateOptions{})
128133
if err != nil {
129134
return nil, fmt.Errorf("failed to Create Role: %w", err)
@@ -136,8 +141,34 @@ func (i *RBACInstaller) ApplyRole(ctx context.Context, role *rbacv1.Role) (*rbac
136141
}
137142

138143
return role, nil
144+
} else if err != nil {
145+
return nil, fmt.Errorf("failed to Get Role: %w", err)
146+
}
147+
148+
existingRole := &rbacv1.Role{}
149+
err = runtime.DefaultUnstructuredConverter.FromUnstructured(existingU.Object, existingRole)
150+
if err != nil {
151+
return nil, fmt.Errorf("failed to convert unstructured to Role: %w", err)
152+
}
153+
154+
modified := false
155+
for _, rule := range role.Rules {
156+
if !ruleExists(existingRole.Rules, rule) {
157+
existingRole.Rules = append(existingRole.Rules, rule)
158+
modified = true
159+
}
139160
}
140161

162+
if !modified {
163+
return existingRole, nil
164+
}
165+
166+
m, err = runtime.DefaultUnstructuredConverter.ToUnstructured(existingRole)
167+
if err != nil {
168+
return nil, fmt.Errorf("failed to convert Role to unstructured: %w", err)
169+
}
170+
u.Object = m
171+
141172
res, err := cli.Update(ctx, u, metav1.UpdateOptions{})
142173
if err != nil {
143174
return nil, fmt.Errorf("failed to Update Role: %w", err)
@@ -180,6 +211,8 @@ func (i *RBACInstaller) ApplyRoleBinding(ctx context.Context, roleBinding *rbacv
180211
}
181212

182213
return roleBinding, nil
214+
} else if err != nil {
215+
return nil, fmt.Errorf("failed to Get RoleBinding: %w", err)
183216
}
184217

185218
res, err := cli.Update(ctx, u, metav1.UpdateOptions{})
@@ -211,7 +244,8 @@ func (i *RBACInstaller) ApplyClusterRole(ctx context.Context, clusterRole *rbacv
211244
},
212245
)
213246

214-
if _, err := cli.Get(ctx, clusterRole.Name, metav1.GetOptions{}); errors.IsNotFound(err) {
247+
existingU, err := cli.Get(ctx, clusterRole.Name, metav1.GetOptions{})
248+
if errors.IsNotFound(err) {
215249
res, err := cli.Create(ctx, u, metav1.CreateOptions{})
216250
if err != nil {
217251
return nil, fmt.Errorf("failed to Create ClusterRole: %w", err)
@@ -224,8 +258,34 @@ func (i *RBACInstaller) ApplyClusterRole(ctx context.Context, clusterRole *rbacv
224258
}
225259

226260
return clusterRole, nil
261+
} else if err != nil {
262+
return nil, fmt.Errorf("failed to Get ClusterRole: %w", err)
227263
}
228264

265+
existingClusterRole := &rbacv1.ClusterRole{}
266+
err = runtime.DefaultUnstructuredConverter.FromUnstructured(existingU.Object, existingClusterRole)
267+
if err != nil {
268+
return nil, fmt.Errorf("failed to convert unstructured to ClusterRole: %w", err)
269+
}
270+
271+
modified := false
272+
for _, rule := range clusterRole.Rules {
273+
if !ruleExists(existingClusterRole.Rules, rule) {
274+
existingClusterRole.Rules = append(existingClusterRole.Rules, rule)
275+
modified = true
276+
}
277+
}
278+
279+
if !modified {
280+
return existingClusterRole, nil
281+
}
282+
283+
m, err = runtime.DefaultUnstructuredConverter.ToUnstructured(existingClusterRole)
284+
if err != nil {
285+
return nil, fmt.Errorf("failed to convert ClusterRole to unstructured: %w", err)
286+
}
287+
u.Object = m
288+
229289
res, err := cli.Update(ctx, u, metav1.UpdateOptions{})
230290
if err != nil {
231291
return nil, fmt.Errorf("failed to Update ClusterRole: %w", err)
@@ -267,6 +327,8 @@ func (i *RBACInstaller) ApplyClusterRoleBinding(ctx context.Context, clusterRole
267327
}
268328

269329
return clusterRoleBinding, nil
330+
} else if err != nil {
331+
return nil, fmt.Errorf("failed to Get ClusterRoleBinding: %w", err)
270332
}
271333

272334
res, err := cli.Update(ctx, u, metav1.UpdateOptions{})
@@ -381,3 +443,12 @@ func (i *RBACInstaller) DeleteClusterRoleBinding(ctx context.Context, name strin
381443
}
382444
return nil
383445
}
446+
447+
func ruleExists(rules []rbacv1.PolicyRule, target rbacv1.PolicyRule) bool {
448+
for _, rule := range rules {
449+
if reflect.DeepEqual(rule, target) {
450+
return true
451+
}
452+
}
453+
return false
454+
}

0 commit comments

Comments
 (0)