@@ -2,12 +2,11 @@ package olm
2
2
3
3
import (
4
4
"context"
5
+ "crypto/sha256"
5
6
"fmt"
6
7
"hash/fnv"
7
- "math"
8
+ "math/big "
8
9
"reflect"
9
- "regexp"
10
- "sort"
11
10
"strings"
12
11
13
12
"github.com/sirupsen/logrus"
@@ -41,8 +40,9 @@ const (
41
40
// kubeResourceNameLimit is the maximum length of a Kubernetes resource name
42
41
kubeResourceNameLimit = 253
43
42
44
- // resourceRandomPrefixLengh is the length of the random prefix added to the resource name
45
- resourceRandomPrefixLength = 5
43
+ // operatorGroupClusterRoleNameFmt template for ClusterRole names owned by OperatorGroups\
44
+ // e.g. olm.og.my-group.admin-<hash>
45
+ operatorGroupClusterRoleNameFmt = "olm.og.%s.%s-%s"
46
46
)
47
47
48
48
var (
@@ -994,30 +994,31 @@ func (a *Operator) updateNamespaceList(op *operatorsv1.OperatorGroup) ([]string,
994
994
return namespaceList , nil
995
995
}
996
996
997
- func (a * Operator ) ensureOpGroupClusterRole (op * operatorsv1.OperatorGroup , roleType string , apis cache.APISet ) error {
997
+ func (a * Operator ) getClusterRoleName (op * operatorsv1.OperatorGroup , roleType string ) (string , error ) {
998
+ roleSuffix := hash (fmt .Sprintf ("%s/%s/%s" , op .GetNamespace (), op .GetName (), roleType ))
999
+ // calculate how many characters are left for the operator group name
1000
+ nameLimit := kubeResourceNameLimit - len (strings .Replace (operatorGroupClusterRoleNameFmt , "%s" , "" , - 1 )) - len (roleType ) - len (roleSuffix )
1001
+ if len (op .GetName ()) < nameLimit {
1002
+ return fmt .Sprintf (operatorGroupClusterRoleNameFmt , op .GetName (), roleType , roleSuffix ), nil
1003
+ }
1004
+ return fmt .Sprintf (operatorGroupClusterRoleNameFmt , op .GetName ()[:nameLimit ], roleType , roleSuffix ), nil
1005
+ }
1006
+
1007
+ func (a * Operator ) ensureOpGroupClusterRole (op * operatorsv1.OperatorGroup , suffix string , apis cache.APISet ) error {
998
1008
// create target cluster role spec
999
1009
var clusterRole * rbacv1.ClusterRole
1000
-
1001
- // to respect kubernetes resource length limitations we need to truncate the operator group name
1002
- template := "olm.og.%s.%s-" // final name will look like, e.g. olm.og.my-og.admin-pll5s
1003
- numTemplateChars := len (strings .Replace (template , "%s" , "" , - 1 ))
1004
- roleTypeLength := len (roleType )
1005
-
1006
- // the operator group component of the name must be limited by the resource name length limit
1007
- // minus the number of characters used up by the other components of the name
1008
- nameLimit := kubeResourceNameLimit - numTemplateChars - roleTypeLength - resourceRandomPrefixLength
1009
- operatorGroupName := op .GetName ()[:int (math .Min (float64 (nameLimit ), float64 (len (op .GetName ()))))]
1010
-
1011
- clusterRoleName := fmt .Sprintf (template , operatorGroupName , roleType )
1012
- aggregationRule , err := a .getClusterRoleAggregationRule (apis , roleType )
1010
+ clusterRoleName , err := a .getClusterRoleName (op , suffix )
1011
+ if err != nil {
1012
+ return err
1013
+ }
1014
+ aggregationRule , err := a .getClusterRoleAggregationRule (apis , suffix )
1013
1015
if err != nil {
1014
1016
return err
1015
1017
}
1016
1018
1017
- // if we'll be creating a fresh cluster role, use generateName to avoid name collisions
1018
1019
clusterRole = & rbacv1.ClusterRole {
1019
1020
ObjectMeta : metav1.ObjectMeta {
1020
- GenerateName : clusterRoleName ,
1021
+ Name : clusterRoleName ,
1021
1022
},
1022
1023
AggregationRule : aggregationRule ,
1023
1024
}
@@ -1026,74 +1027,32 @@ func (a *Operator) ensureOpGroupClusterRole(op *operatorsv1.OperatorGroup, roleT
1026
1027
return err
1027
1028
}
1028
1029
1029
- // list current cluster roles owned by this operator group
1030
- clusterRoles , err := a .lister .RbacV1 ().ClusterRoleLister ().List ( ownerutil . OperatorGroupOwnerSelector ( op ) )
1031
- if err != nil {
1030
+ // get existing cluster role for this level (suffix: admin, edit, view))
1031
+ existingRole , err := a .lister .RbacV1 ().ClusterRoleLister ().Get ( clusterRoleName )
1032
+ if err != nil && ! apierrors . IsNotFound ( err ) {
1032
1033
return err
1033
1034
}
1034
1035
1035
- // find the cluster role that matches the template and is of the correct type
1036
- var existingClusterRoles []* rbacv1.ClusterRole
1037
- re , ok := ogClusterRoleNameRegExMap [roleType ]
1038
- if ! ok {
1039
- return fmt .Errorf ("no regex found for role type: %s" , roleType )
1040
- }
1041
- for _ , cr := range clusterRoles {
1042
- if re .FindStringSubmatch (cr .GetName ()) != nil {
1043
- existingClusterRoles = append (existingClusterRoles , cr )
1044
- }
1045
- }
1046
-
1047
- switch len (existingClusterRoles ) {
1048
- // if no cluster role exists, create one
1049
- case 0 :
1050
- a .logger .Infof ("Creating cluster role: %s owned by operator group: %s/%s" , clusterRole .GetGenerateName (), op .GetNamespace (), op .GetName ())
1051
- if _ , err = a .opClient .KubernetesInterface ().RbacV1 ().ClusterRoles ().Create (context .TODO (), clusterRole , metav1.CreateOptions {}); err != nil {
1052
- // name collision, try again with a new name in the next reconcile
1053
- a .logger .WithError (err ).Errorf ("Create cluster role failed: %v" , clusterRole )
1054
- }
1055
- return err
1056
- // if an existing cluster role resource is found, update it if necessary
1057
- case 1 :
1058
- existingClusterRole := existingClusterRoles [0 ].DeepCopy ()
1059
- // the cluster role will need to be updated if the aggregation rules have changed - otherwise, nothing to do
1060
- // the resource is guaranteed to have the correct ownership labels because we reached this part of the code
1061
- if ! reflect .DeepEqual (existingClusterRole .AggregationRule , aggregationRule ) {
1062
- if _ , err := a .opClient .UpdateClusterRole (existingClusterRole ); err != nil {
1063
- a .logger .WithError (err ).Errorf ("Update existing cluster role failed: %v" , clusterRole )
1064
- }
1036
+ if existingRole != nil {
1037
+ // if the existing role conforms to the naming convention, check for skew
1038
+ cp := existingRole .DeepCopy ()
1039
+ if err := ownerutil .AddOwnerLabels (cp , op ); err != nil {
1065
1040
return err
1066
1041
}
1067
- // the inherent race condition created by listing to check if a resource exists and then creating it
1068
- // and the fact that we are using generateName means that it is possible for multiple cluster roles of the same
1069
- // role type to be created for the same operator group. In this case, we'll disambiguate by keeping the oldest
1070
- // cluster role (by creation timestamp - tie-breaking by name) and deleting the others
1071
- default :
1072
- a .logger .Warnf ("multiple (%d) cluster roles of type %s owned by operator group %s/%s found" , len (existingClusterRoles ), roleType , op .GetNamespace (), op .GetName ())
1073
- // sort by creation timestamp and tie-break by name
1074
- sort .Slice (existingClusterRoles , func (i , j int ) bool {
1075
- creationTimeI := existingClusterRoles [i ].GetCreationTimestamp ()
1076
- creationTimeJ := existingClusterRoles [j ].GetCreationTimestamp ()
1077
- if creationTimeI .Equal (& creationTimeJ ) {
1078
- return strings .Compare (existingClusterRoles [i ].GetName (), existingClusterRoles [j ].GetName ()) < 0
1079
- }
1080
- return creationTimeI .Before (& creationTimeJ )
1081
- })
1082
1042
1083
- // delete all but the oldest cluster role
1084
- a .logger .Infof ("keeping cluster role: %s owned by operator group: %s/%s and deleting %d others" , existingClusterRoles [0 ].GetName (), op .GetNamespace (), op .GetName (), len (existingClusterRoles )- 1 )
1085
- for _ , cr := range existingClusterRoles [1 :] {
1086
- a .logger .Infof ("deleting cluster role: %s owned by operator group: %s/%s - there may be only one" , cr .GetName (), op .GetNamespace (), op .GetName ())
1087
- if err := a .opClient .DeleteClusterRole (cr .GetName (), & metav1.DeleteOptions {}); err != nil {
1088
- a .logger .WithError (err ).Errorf ("Delete cluster role failed: %v" , cr )
1089
- return err
1090
- }
1043
+ // ensure that the labels and aggregation rules are correct
1044
+ if labels .Equals (existingRole .Labels , cp .Labels ) && reflect .DeepEqual (existingRole .AggregationRule , aggregationRule ) {
1045
+ return nil
1091
1046
}
1092
-
1093
- // now that we're (hopefully) down to one cluster role, re-reconcile
1094
- return fmt .Errorf ("multiple cluster roles of type %s owned by operator group %s/%s found" , roleType , op .GetNamespace (), op .GetName ())
1047
+ if _ , err := a .opClient .UpdateClusterRole (clusterRole ); err != nil {
1048
+ a .logger .WithError (err ).Errorf ("update existing cluster role failed: %v" , clusterRole )
1049
+ }
1050
+ return err
1095
1051
}
1096
- return nil
1052
+
1053
+ a .logger .Infof ("creating cluster role: %s owned by operator group: %s/%s" , clusterRole .GetName (), op .GetNamespace (), op .GetName ())
1054
+ _ , err = a .opClient .KubernetesInterface ().RbacV1 ().ClusterRoles ().Create (context .TODO (), clusterRole , metav1.CreateOptions {})
1055
+ return err
1097
1056
}
1098
1057
1099
1058
func (a * Operator ) getClusterRoleAggregationRule (apis cache.APISet , suffix string ) (* rbacv1.AggregationRule , error ) {
@@ -1212,14 +1171,12 @@ func csvCopyPrototype(src, dst *v1alpha1.ClusterServiceVersion) {
1212
1171
dst .Status .Message = fmt .Sprintf ("The operator is running in %s but is managing this namespace" , src .GetNamespace ())
1213
1172
}
1214
1173
1215
- // ogClusterRoleNameRegEx returns a regexp for the naming format used for cluster roles owned by operator groups
1216
- // for a particular role type e.g. olm.og.my-og.admin-pll2k (where pll2k is a random suffix and admin is the role type)
1217
- var ogClusterRoleNameRegExMap = map [string ]* regexp.Regexp {
1218
- "admin" : ogClusterRoleNameRegEx ("admin" ),
1219
- "edit" : ogClusterRoleNameRegEx ("edit" ),
1220
- "view" : ogClusterRoleNameRegEx ("view" ),
1174
+ func hash (s string ) string {
1175
+ return toBase62 (sha256 .Sum224 ([]byte (s )))
1221
1176
}
1222
1177
1223
- func ogClusterRoleNameRegEx (roleType string ) * regexp.Regexp {
1224
- return regexp .MustCompile (fmt .Sprintf (`^olm\.og\.[^\.]+\.%s-[a-z0-9]{5}$` , roleType ))
1178
+ func toBase62 (hash [28 ]byte ) string {
1179
+ var i big.Int
1180
+ i .SetBytes (hash [:])
1181
+ return i .Text (62 )
1225
1182
}
0 commit comments