Skip to content

Commit f895696

Browse files
author
Per Goncalves da Silva
committed
refactor operator group cluster role name
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent 11b66ef commit f895696

File tree

4 files changed

+99
-26
lines changed

4 files changed

+99
-26
lines changed

Diff for: go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ require (
1414
github.com/go-logr/logr v1.2.4
1515
github.com/golang/mock v1.6.0
1616
github.com/google/go-cmp v0.5.9
17+
github.com/google/uuid v1.3.0
1718
github.com/googleapis/gnostic v0.5.5
1819
github.com/itchyny/gojq v0.11.0
1920
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2
@@ -124,7 +125,6 @@ require (
124125
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
125126
github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 // indirect
126127
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
127-
github.com/google/uuid v1.3.0 // indirect
128128
github.com/gorilla/mux v1.8.0 // indirect
129129
github.com/gosuri/uitable v0.0.4 // indirect
130130
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect

Diff for: pkg/controller/operators/olm/operatorgroup.go

+89-22
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package olm
33
import (
44
"context"
55
"fmt"
6+
"github.com/google/uuid"
67
"hash/fnv"
78
"reflect"
89
"strings"
@@ -978,32 +979,18 @@ func (a *Operator) updateNamespaceList(op *operatorsv1.OperatorGroup) ([]string,
978979
}
979980

980981
func (a *Operator) ensureOpGroupClusterRole(op *operatorsv1.OperatorGroup, suffix string, apis cache.APISet) error {
981-
clusterRole := &rbacv1.ClusterRole{
982-
ObjectMeta: metav1.ObjectMeta{
983-
Name: strings.Join([]string{op.GetName(), suffix}, "-"),
984-
},
985-
}
986-
var selectors []metav1.LabelSelector
987-
for api := range apis {
988-
aggregationLabel, err := aggregationLabelFromAPIKey(api, suffix)
989-
if err != nil {
990-
return err
991-
}
992-
selectors = append(selectors, metav1.LabelSelector{
993-
MatchLabels: map[string]string{
994-
aggregationLabel: "true",
995-
},
996-
})
997-
}
998-
if len(selectors) > 0 {
999-
clusterRole.AggregationRule = &rbacv1.AggregationRule{
1000-
ClusterRoleSelectors: selectors,
1001-
}
982+
clusterRole, err := a.getOpGroupClusterRole(op, suffix, apis)
983+
if err != nil {
984+
return err
1002985
}
1003-
err := ownerutil.AddOwnerLabels(clusterRole, op)
986+
aggregationRule, err := a.getClusterRoleAggregationRule(apis, suffix)
1004987
if err != nil {
1005988
return err
1006989
}
990+
clusterRole.AggregationRule = aggregationRule
991+
if err := ownerutil.AddOwnerLabels(clusterRole, op); err != nil {
992+
return err
993+
}
1007994

1008995
existingRole, err := a.lister.RbacV1().ClusterRoleLister().Get(clusterRole.Name)
1009996
if err != nil && !apierrors.IsNotFound(err) {
@@ -1031,6 +1018,86 @@ func (a *Operator) ensureOpGroupClusterRole(op *operatorsv1.OperatorGroup, suffi
10311018
return nil
10321019
}
10331020

1021+
func hash(str string) (string, error) {
1022+
// hash the string with some randomness to help avoid guessing attacks
1023+
h := fnv.New32a()
1024+
rnd := uuid.NewString()
1025+
if _, err := h.Write([]byte(fmt.Sprintf("%s.%s", rnd, str))); err != nil {
1026+
return "", err
1027+
}
1028+
return strings.Trim(fmt.Sprintf("%016x", h.Sum32()), "0"), nil
1029+
}
1030+
1031+
func (a *Operator) getOpGroupClusterRole(op *operatorsv1.OperatorGroup, suffix string, apis cache.APISet) (*rbacv1.ClusterRole, error) {
1032+
// oldRoleName := strings.Join([]string{op.GetName(), suffix}, "-")
1033+
roleNameHash, err := hash(fmt.Sprintf("%s/%s", op.GetNamespace(), op.GetName()))
1034+
if err != nil {
1035+
return nil, err
1036+
}
1037+
roleName := fmt.Sprintf("olm.operator-group.role.%s.%s", roleNameHash, suffix)
1038+
1039+
clusterRole := &rbacv1.ClusterRole{
1040+
ObjectMeta: metav1.ObjectMeta{
1041+
Name: roleName,
1042+
},
1043+
}
1044+
1045+
// check if old role name exists
1046+
roles, err := a.lister.RbacV1().ClusterRoleLister().List(ownerutil.ClusterRoleSelector(op))
1047+
if err != nil && !apierrors.IsNotFound(err) {
1048+
return nil, err
1049+
}
1050+
var suffixRoles []*rbacv1.ClusterRole
1051+
for idx, _ := range roles {
1052+
role := roles[idx]
1053+
if strings.HasSuffix(role.GetName(), suffix) {
1054+
suffixRoles = append(suffixRoles, role)
1055+
}
1056+
}
1057+
// todo: finding multiple owned cluster roles with the same suffix is highly unlikely to happen. However,
1058+
// we need to test and document the manual clean up in case it ever happens
1059+
if len(suffixRoles) > 1 {
1060+
err := fmt.Errorf("found multiple cluster roles with suffix %s, please clean up manually", suffix)
1061+
a.logger.Error(err)
1062+
return nil, err
1063+
} else if len(suffixRoles) == 1 {
1064+
return suffixRoles[0], nil
1065+
}
1066+
return clusterRole, nil
1067+
}
1068+
1069+
func (a *Operator) getClusterRoleAggregationRule(apis cache.APISet, suffix string) (*rbacv1.AggregationRule, error) {
1070+
var selectors []metav1.LabelSelector
1071+
for api := range apis {
1072+
aggregationLabel, err := aggregationLabelFromAPIKey(api, suffix)
1073+
if err != nil {
1074+
return nil, err
1075+
}
1076+
selectors = append(selectors, metav1.LabelSelector{
1077+
MatchLabels: map[string]string{
1078+
aggregationLabel: "true",
1079+
},
1080+
})
1081+
}
1082+
if len(selectors) > 0 {
1083+
return &rbacv1.AggregationRule{
1084+
ClusterRoleSelectors: selectors,
1085+
}, nil
1086+
}
1087+
return nil, nil
1088+
}
1089+
1090+
func (a *Operator) clusterRoleExistsAndIsOwnedBy(roleName string, owner ownerutil.Owner) (bool, error) {
1091+
role, err := a.lister.RbacV1().ClusterRoleLister().Get(roleName)
1092+
if err != nil && !apierrors.IsNotFound(err) {
1093+
return false, err
1094+
}
1095+
if apierrors.IsNotFound(err) {
1096+
return false, nil
1097+
}
1098+
return ownerutil.IsOwnedBy(role, owner), nil
1099+
}
1100+
10341101
func (a *Operator) ensureOpGroupClusterRoles(op *operatorsv1.OperatorGroup, apis cache.APISet) error {
10351102
for _, suffix := range Suffices {
10361103
if err := a.ensureOpGroupClusterRole(op, suffix, apis); err != nil {

Diff for: pkg/lib/ownerutil/util.go

+5
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,11 @@ func CSVOwnerSelector(owner *operatorsv1alpha1.ClusterServiceVersion) labels.Sel
272272
return labels.SelectorFromSet(OwnerLabel(owner, operatorsv1alpha1.ClusterServiceVersionKind))
273273
}
274274

275+
// ClusterRoleSelector returns a label selector to find cluster role objects owned by owner
276+
func ClusterRoleSelector(owner *operatorsv1.OperatorGroup) labels.Selector {
277+
return labels.SelectorFromSet(OwnerLabel(owner, operatorsv1.OperatorGroupKind))
278+
}
279+
275280
// AddOwner adds an owner to the ownerref list.
276281
func AddOwner(object metav1.Object, owner Owner, blockOwnerDeletion, isController bool) {
277282
// Most of the time we won't have TypeMeta on the object, so we infer it for types we know about

Diff for: test/e2e/operator_groups_e2e_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -340,21 +340,22 @@ var _ = Describe("Operator Group", func() {
340340
})
341341

342342
// validate provided API clusterroles for the operatorgroup
343-
adminRole, err := c.KubernetesInterface().RbacV1().ClusterRoles().Get(context.TODO(), operatorGroup.Name+"-admin", metav1.GetOptions{})
343+
roleNamePrefix := fmt.Sprintf("olm.%s.operator-group.%s.role.", opGroupNamespace, operatorGroup.Name)
344+
adminRole, err := c.KubernetesInterface().RbacV1().ClusterRoles().Get(context.TODO(), roleNamePrefix+"admin", metav1.GetOptions{})
344345
require.NoError(GinkgoT(), err)
345346
adminPolicyRules := []rbacv1.PolicyRule{
346347
{Verbs: []string{"*"}, APIGroups: []string{mainCRD.Spec.Group}, Resources: []string{mainCRDPlural}},
347348
}
348349
require.Equal(GinkgoT(), adminPolicyRules, adminRole.Rules)
349350

350-
editRole, err := c.KubernetesInterface().RbacV1().ClusterRoles().Get(context.TODO(), operatorGroup.Name+"-edit", metav1.GetOptions{})
351+
editRole, err := c.KubernetesInterface().RbacV1().ClusterRoles().Get(context.TODO(), roleNamePrefix+"edit", metav1.GetOptions{})
351352
require.NoError(GinkgoT(), err)
352353
editPolicyRules := []rbacv1.PolicyRule{
353354
{Verbs: []string{"create", "update", "patch", "delete"}, APIGroups: []string{mainCRD.Spec.Group}, Resources: []string{mainCRDPlural}},
354355
}
355356
require.Equal(GinkgoT(), editPolicyRules, editRole.Rules)
356357

357-
viewRole, err := c.KubernetesInterface().RbacV1().ClusterRoles().Get(context.TODO(), operatorGroup.Name+"-view", metav1.GetOptions{})
358+
viewRole, err := c.KubernetesInterface().RbacV1().ClusterRoles().Get(context.TODO(), roleNamePrefix+"view", metav1.GetOptions{})
358359
require.NoError(GinkgoT(), err)
359360
viewPolicyRules := []rbacv1.PolicyRule{
360361
{Verbs: []string{"get"}, APIGroups: []string{"apiextensions.k8s.io"}, Resources: []string{"customresourcedefinitions"}, ResourceNames: []string{mainCRD.Name}},

0 commit comments

Comments
 (0)