Skip to content

Commit 7dc56de

Browse files
Merge pull request #1377 from openshift-cherrypick-robot/cherry-pick-1348-to-release-4.4
[release-4.4] Bug 1809568: Make provided API ClusterRoles be owned by the corresponding API.
2 parents e024964 + c388bc5 commit 7dc56de

File tree

5 files changed

+134
-56
lines changed

5 files changed

+134
-56
lines changed

pkg/api/apis/operators/operatorgroup_types.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ type OperatorGroupList struct {
7474
}
7575

7676
func (o *OperatorGroup) BuildTargetNamespaces() string {
77-
sort.Strings(o.Status.Namespaces)
78-
return strings.Join(o.Status.Namespaces, ",")
77+
ns := make([]string, len(o.Status.Namespaces))
78+
copy(ns, o.Status.Namespaces)
79+
sort.Strings(ns)
80+
return strings.Join(ns, ",")
7981
}

pkg/api/apis/operators/v1/operatorgroup_types.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,10 @@ type OperatorGroupList struct {
7474
}
7575

7676
func (o *OperatorGroup) BuildTargetNamespaces() string {
77-
sort.Strings(o.Status.Namespaces)
78-
return strings.Join(o.Status.Namespaces, ",")
77+
ns := make([]string, len(o.Status.Namespaces))
78+
copy(ns, o.Status.Namespaces)
79+
sort.Strings(ns)
80+
return strings.Join(ns, ",")
7981
}
8082

8183
// IsServiceAccountSpecified returns true if the spec has a service account name specified.

pkg/controller/operators/olm/operator.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,7 @@ func (a *Operator) operatorGroupFromAnnotations(logger *logrus.Entry, csv *v1alp
10931093
}
10941094

10951095
// Target namespaces don't match
1096-
if targets != strings.Join(operatorGroup.Status.Namespaces, ",") {
1096+
if targets != operatorGroup.BuildTargetNamespaces() {
10971097
logger.Info("olm.targetNamespaces annotation doesn't match operatorgroup status")
10981098
return nil
10991099
}
@@ -1105,21 +1105,21 @@ func (a *Operator) operatorGroupForCSV(csv *v1alpha1.ClusterServiceVersion, logg
11051105
now := a.now()
11061106

11071107
// Attempt to associate an OperatorGroup with the CSV.
1108-
operatorGroups, err := a.client.OperatorsV1().OperatorGroups(csv.GetNamespace()).List(metav1.ListOptions{})
1108+
operatorGroups, err := a.lister.OperatorsV1().OperatorGroupLister().OperatorGroups(csv.GetNamespace()).List(labels.Everything())
11091109
if err != nil {
11101110
logger.Errorf("error occurred while attempting to associate csv with operatorgroup")
11111111
return nil, err
11121112
}
11131113
var operatorGroup *v1.OperatorGroup
11141114

1115-
switch len(operatorGroups.Items) {
1115+
switch len(operatorGroups) {
11161116
case 0:
11171117
err = fmt.Errorf("csv in namespace with no operatorgroups")
11181118
logger.Warn(err)
11191119
csv.SetPhaseWithEvent(v1alpha1.CSVPhaseFailed, v1alpha1.CSVReasonNoOperatorGroup, err.Error(), now, a.recorder)
11201120
return nil, err
11211121
case 1:
1122-
operatorGroup = &operatorGroups.Items[0]
1122+
operatorGroup = operatorGroups[0]
11231123
logger = logger.WithField("opgroup", operatorGroup.GetName())
11241124
if a.operatorGroupAnnotationsDiffer(&csv.ObjectMeta, operatorGroup) {
11251125
a.setOperatorGroupAnnotations(&csv.ObjectMeta, operatorGroup, true)
@@ -1223,13 +1223,16 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v
12231223
}
12241224

12251225
// Check for intersecting provided APIs in intersecting OperatorGroups
1226-
options := metav1.ListOptions{
1227-
FieldSelector: fmt.Sprintf("metadata.name!=%s,metadata.namespace!=%s", operatorGroup.GetName(), operatorGroup.GetNamespace()),
1226+
allGroups, err := a.lister.OperatorsV1().OperatorGroupLister().List(labels.Everything())
1227+
otherGroups := make([]v1.OperatorGroup, 0, len(allGroups))
1228+
for _, g := range allGroups {
1229+
if g.GetName() != operatorGroup.GetName() || g.GetNamespace() != operatorGroup.GetNamespace() {
1230+
otherGroups = append(otherGroups, *g)
1231+
}
12281232
}
1229-
otherGroups, err := a.client.OperatorsV1().OperatorGroups(metav1.NamespaceAll).List(options)
12301233

12311234
groupSurface := resolver.NewOperatorGroup(operatorGroup)
1232-
otherGroupSurfaces := resolver.NewOperatorGroupSurfaces(otherGroups.Items...)
1235+
otherGroupSurfaces := resolver.NewOperatorGroupSurfaces(otherGroups...)
12331236
providedAPIs := operatorSurface.ProvidedAPIs().StripPlural()
12341237

12351238
switch result := a.apiReconciler.Reconcile(providedAPIs, groupSurface, otherGroupSurfaces...); {

pkg/controller/operators/olm/operatorgroup.go

Lines changed: 94 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@ import (
1919
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
2020
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
2121
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
22+
"github.com/operator-framework/operator-registry/pkg/registry"
2223
opregistry "github.com/operator-framework/operator-registry/pkg/registry"
2324
)
2425

2526
const (
26-
operatorGroupAggregrationKeyPrefix = "olm.opgroup.permissions/aggregate-to-"
27-
kubeRBACAggregationKeyPrefix = "rbac.authorization.k8s.io/aggregate-to-"
28-
AdminSuffix = "admin"
29-
EditSuffix = "edit"
30-
ViewSuffix = "view"
27+
AdminSuffix = "admin"
28+
EditSuffix = "edit"
29+
ViewSuffix = "view"
3130
)
3231

3332
var (
@@ -42,6 +41,14 @@ var (
4241
}
4342
)
4443

44+
func aggregationLabelFromAPIKey(k opregistry.APIKey, suffix string) (string, error) {
45+
hash, err := resolver.APIKeyToGVKHash(k)
46+
if err != nil {
47+
return "", err
48+
}
49+
return fmt.Sprintf("olm.opgroup.permissions/aggregate-to-%s-%s", hash, suffix), nil
50+
}
51+
4552
func (a *Operator) syncOperatorGroups(obj interface{}) error {
4653
op, ok := obj.(*v1.OperatorGroup)
4754
if !ok {
@@ -115,12 +122,6 @@ func (a *Operator) syncOperatorGroups(obj interface{}) error {
115122
}
116123
logger.Debug("OperatorGroup CSV annotation completed")
117124

118-
if err := a.ensureOpGroupClusterRoles(op); err != nil {
119-
logger.WithError(err).Warn("failed to ensure operatorgroup clusterroles")
120-
return err
121-
}
122-
logger.Debug("operatorgroup clusterroles ensured")
123-
124125
// Requeue all CSVs that provide the same APIs (including those removed). This notifies conflicting CSVs in
125126
// intersecting groups that their conflict has possibly been resolved, either through resizing or through
126127
// deletion of the conflicting CSV.
@@ -135,6 +136,12 @@ func (a *Operator) syncOperatorGroups(obj interface{}) error {
135136
providedAPIsForGroup[api] = struct{}{}
136137
}
137138

139+
if err := a.ensureOpGroupClusterRoles(op, providedAPIsForGroup); err != nil {
140+
logger.WithError(err).Warn("failed to ensure operatorgroup clusterroles")
141+
return err
142+
}
143+
logger.Debug("operatorgroup clusterroles ensured")
144+
138145
csvs, err := a.findCSVsThatProvideAnyOf(providedAPIsForGroup)
139146
if err != nil {
140147
logger.WithError(err).Warn("could not find csvs that provide group apis")
@@ -295,32 +302,43 @@ func (a *Operator) pruneProvidedAPIs(group *v1.OperatorGroup, groupProvidedAPIs
295302
}
296303

297304
// ensureProvidedAPIClusterRole ensures that a clusterrole exists (admin, edit, or view) for a single provided API Type
298-
func (a *Operator) ensureProvidedAPIClusterRole(operatorGroup *v1.OperatorGroup, csv *v1alpha1.ClusterServiceVersion, namePrefix, suffix string, verbs []string, group, resource string, resourceNames []string) error {
305+
func (a *Operator) ensureProvidedAPIClusterRole(operatorGroup *v1.OperatorGroup, csv *v1alpha1.ClusterServiceVersion, namePrefix, suffix string, verbs []string, group, resource string, resourceNames []string, api ownerutil.Owner, key opregistry.APIKey) error {
306+
aggregationLabel, err := aggregationLabelFromAPIKey(key, suffix)
307+
if err != nil {
308+
return err
309+
}
299310
clusterRole := &rbacv1.ClusterRole{
300311
ObjectMeta: metav1.ObjectMeta{
301312
Name: namePrefix + suffix,
302313
Labels: map[string]string{
303-
kubeRBACAggregationKeyPrefix + suffix: "true",
304-
operatorGroupAggregrationKeyPrefix + suffix: operatorGroup.GetName(),
314+
// Matches aggregation rules on the bootstrap ClusterRoles.
315+
// https://github.com/kubernetes/kubernetes/blob/61847eab61788fb0543b4cf147773c9da646ed2f/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go#L232
316+
fmt.Sprintf("rbac.authorization.k8s.io/aggregate-to-%s", suffix): "true",
317+
aggregationLabel: "true",
305318
},
319+
OwnerReferences: []metav1.OwnerReference{ownerutil.NonBlockingOwner(api)},
306320
},
307321
Rules: []rbacv1.PolicyRule{{Verbs: verbs, APIGroups: []string{group}, Resources: []string{resource}, ResourceNames: resourceNames}},
308322
}
309-
err := ownerutil.AddOwnerLabels(clusterRole, csv)
310-
if err != nil {
311-
return err
312-
}
313-
existingCR, err := a.opClient.KubernetesInterface().RbacV1().ClusterRoles().Create(clusterRole)
314-
if k8serrors.IsAlreadyExists(err) {
315-
if existingCR != nil && reflect.DeepEqual(existingCR.Labels, clusterRole.Labels) && reflect.DeepEqual(existingCR.Rules, clusterRole.Rules) {
323+
324+
existingCR, err := a.lister.RbacV1().ClusterRoleLister().Get(clusterRole.Name)
325+
if existingCR == nil {
326+
existingCR, err = a.opClient.KubernetesInterface().RbacV1().ClusterRoles().Create(clusterRole)
327+
if err == nil {
316328
return nil
317329
}
318-
if _, err = a.opClient.UpdateClusterRole(clusterRole); err != nil {
319-
a.logger.WithError(err).Errorf("Update existing cluster role failed: %v", clusterRole)
330+
if !k8serrors.IsAlreadyExists(err) {
331+
a.logger.WithError(err).Errorf("Create cluster role failed: %v", clusterRole)
320332
return err
321333
}
322-
} else if err != nil {
323-
a.logger.WithError(err).Errorf("Create cluster role failed: %v", clusterRole)
334+
}
335+
336+
if existingCR != nil && reflect.DeepEqual(existingCR.Rules, clusterRole.Rules) && ownerutil.IsOwnedBy(existingCR, api) && labels.Equals(existingCR.Labels, clusterRole.Labels) {
337+
return nil
338+
}
339+
340+
if _, err := a.opClient.UpdateClusterRole(clusterRole); err != nil {
341+
a.logger.WithError(err).Errorf("Update existing cluster role failed: %v", clusterRole)
324342
return err
325343
}
326344
return nil
@@ -329,27 +347,37 @@ func (a *Operator) ensureProvidedAPIClusterRole(operatorGroup *v1.OperatorGroup,
329347
// ensureClusterRolesForCSV ensures that ClusterRoles for writing and reading provided APIs exist for each operator
330348
func (a *Operator) ensureClusterRolesForCSV(csv *v1alpha1.ClusterServiceVersion, operatorGroup *v1.OperatorGroup) error {
331349
for _, owned := range csv.Spec.CustomResourceDefinitions.Owned {
350+
crd, err := a.lister.APIExtensionsV1beta1().CustomResourceDefinitionLister().Get(owned.Name)
351+
if err != nil {
352+
return fmt.Errorf("crd %q not found: %s", owned.Name, err.Error())
353+
}
332354
nameGroupPair := strings.SplitN(owned.Name, ".", 2) // -> etcdclusters etcd.database.coreos.com
333355
if len(nameGroupPair) != 2 {
334356
return fmt.Errorf("invalid parsing of name '%v', got %v", owned.Name, nameGroupPair)
335357
}
336358
plural := nameGroupPair[0]
337359
group := nameGroupPair[1]
338360
namePrefix := fmt.Sprintf("%s-%s-", owned.Name, owned.Version)
339-
361+
key := registry.APIKey{Group: group, Version: owned.Version, Kind: owned.Kind, Plural: plural}
340362
for suffix, verbs := range VerbsForSuffix {
341-
if err := a.ensureProvidedAPIClusterRole(operatorGroup, csv, namePrefix, suffix, verbs, group, plural, nil); err != nil {
363+
if err := a.ensureProvidedAPIClusterRole(operatorGroup, csv, namePrefix, suffix, verbs, group, plural, nil, crd, key); err != nil {
342364
return err
343365
}
344366
}
345-
if err := a.ensureProvidedAPIClusterRole(operatorGroup, csv, namePrefix+"crd", ViewSuffix, []string{"get"}, "apiextensions.k8s.io", "customresourcedefinitions", []string{owned.Name}); err != nil {
367+
if err := a.ensureProvidedAPIClusterRole(operatorGroup, csv, namePrefix+"crd", ViewSuffix, []string{"get"}, "apiextensions.k8s.io", "customresourcedefinitions", []string{owned.Name}, crd, key); err != nil {
346368
return err
347369
}
348370
}
349371
for _, owned := range csv.Spec.APIServiceDefinitions.Owned {
372+
svcName := strings.Join([]string{owned.Version, owned.Group}, ".")
373+
svc, err := a.lister.APIRegistrationV1().APIServiceLister().Get(svcName)
374+
if err != nil {
375+
return fmt.Errorf("apiservice %q not found: %s", svcName, err.Error())
376+
}
350377
namePrefix := fmt.Sprintf("%s-%s-", owned.Name, owned.Version)
378+
key := registry.APIKey{Group: owned.Group, Version: owned.Version, Kind: owned.Kind}
351379
for suffix, verbs := range VerbsForSuffix {
352-
if err := a.ensureProvidedAPIClusterRole(operatorGroup, csv, namePrefix, suffix, verbs, owned.Group, owned.Name, nil); err != nil {
380+
if err := a.ensureProvidedAPIClusterRole(operatorGroup, csv, namePrefix, suffix, verbs, owned.Group, owned.Name, nil, svc, key); err != nil {
353381
return err
354382
}
355383
}
@@ -853,38 +881,60 @@ func (a *Operator) updateNamespaceList(op *v1.OperatorGroup) ([]string, error) {
853881
return namespaceList, nil
854882
}
855883

856-
func (a *Operator) ensureOpGroupClusterRole(op *v1.OperatorGroup, suffix string) error {
884+
func (a *Operator) ensureOpGroupClusterRole(op *v1.OperatorGroup, suffix string, apis resolver.APISet) error {
857885
clusterRole := &rbacv1.ClusterRole{
858886
ObjectMeta: metav1.ObjectMeta{
859887
Name: strings.Join([]string{op.GetName(), suffix}, "-"),
860888
},
861-
AggregationRule: &rbacv1.AggregationRule{
862-
ClusterRoleSelectors: []metav1.LabelSelector{
863-
{
864-
MatchLabels: map[string]string{
865-
operatorGroupAggregrationKeyPrefix + suffix: op.GetName(),
866-
},
867-
},
889+
}
890+
var selectors []metav1.LabelSelector
891+
for api := range apis {
892+
aggregationLabel, err := aggregationLabelFromAPIKey(api, suffix)
893+
if err != nil {
894+
return err
895+
}
896+
selectors = append(selectors, metav1.LabelSelector{
897+
MatchLabels: map[string]string{
898+
aggregationLabel: "true",
868899
},
869-
},
900+
})
901+
}
902+
if len(selectors) > 0 {
903+
clusterRole.AggregationRule = &rbacv1.AggregationRule{
904+
ClusterRoleSelectors: selectors,
905+
}
870906
}
871907
err := ownerutil.AddOwnerLabels(clusterRole, op)
872908
if err != nil {
873909
return err
874910
}
875-
_, err = a.opClient.KubernetesInterface().RbacV1().ClusterRoles().Create(clusterRole)
876-
if k8serrors.IsAlreadyExists(err) {
911+
912+
existingRole, err := a.lister.RbacV1().ClusterRoleLister().Get(clusterRole.Name)
913+
if existingRole == nil {
914+
existingRole, err = a.opClient.KubernetesInterface().RbacV1().ClusterRoles().Create(clusterRole)
915+
if err == nil {
916+
return nil
917+
}
918+
if !k8serrors.IsAlreadyExists(err) {
919+
a.logger.WithError(err).Errorf("Create cluster role failed: %v", clusterRole)
920+
return err
921+
}
922+
}
923+
924+
if existingRole != nil && labels.Equals(existingRole.Labels, clusterRole.Labels) && reflect.DeepEqual(existingRole.AggregationRule, clusterRole.AggregationRule) {
877925
return nil
878-
} else if err != nil {
879-
a.logger.WithError(err).Errorf("Create cluster role failed: %v", clusterRole)
926+
}
927+
928+
if _, err := a.opClient.UpdateClusterRole(clusterRole); err != nil {
929+
a.logger.WithError(err).Errorf("Update existing cluster role failed: %v", clusterRole)
880930
return err
881931
}
882932
return nil
883933
}
884934

885-
func (a *Operator) ensureOpGroupClusterRoles(op *v1.OperatorGroup) error {
935+
func (a *Operator) ensureOpGroupClusterRoles(op *v1.OperatorGroup, apis resolver.APISet) error {
886936
for _, suffix := range Suffices {
887-
if err := a.ensureOpGroupClusterRole(op, suffix); err != nil {
937+
if err := a.ensureOpGroupClusterRole(op, suffix, apis); err != nil {
888938
return err
889939
}
890940
}

pkg/lib/ownerutil/util.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ import (
99
log "github.com/sirupsen/logrus"
1010
corev1 "k8s.io/api/core/v1"
1111
rbac "k8s.io/api/rbac/v1"
12+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
13+
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
1214
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1315
"k8s.io/apimachinery/pkg/labels"
1416
"k8s.io/apimachinery/pkg/runtime"
1517
"k8s.io/apimachinery/pkg/runtime/schema"
18+
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
1619
)
1720

1821
const (
@@ -365,6 +368,24 @@ func InferGroupVersionKind(obj runtime.Object) error {
365368
Version: v1.GroupVersion,
366369
Kind: "OperatorGroup",
367370
})
371+
case *apiregistrationv1.APIService:
372+
objectKind.SetGroupVersionKind(schema.GroupVersionKind{
373+
Group: apiregistrationv1.GroupName,
374+
Version: apiregistrationv1.SchemeGroupVersion.Version,
375+
Kind: "APIService",
376+
})
377+
case *apiextensionsv1beta1.CustomResourceDefinition:
378+
objectKind.SetGroupVersionKind(schema.GroupVersionKind{
379+
Group: apiextensionsv1beta1.GroupName,
380+
Version: apiextensionsv1beta1.SchemeGroupVersion.Version,
381+
Kind: "CustomResourceDefinition",
382+
})
383+
case *apiextensionsv1.CustomResourceDefinition:
384+
objectKind.SetGroupVersionKind(schema.GroupVersionKind{
385+
Group: apiextensionsv1.GroupName,
386+
Version: apiextensionsv1.SchemeGroupVersion.Version,
387+
Kind: "CustomResourceDefinition",
388+
})
368389
default:
369390
return fmt.Errorf("could not infer GVK for object: %#v, %#v", obj, objectKind)
370391
}

0 commit comments

Comments
 (0)