Skip to content

Commit 969a7e0

Browse files
Merge pull request #1017 from openshift-cherrypick-robot/cherry-pick-1015-to-release-4.18
[release-4.18] OCPBUGS-57314: operatorgroup: ensure clusterroleselectors in clusterrole aggregation rules are sorted
2 parents d83b756 + 9711387 commit 969a7e0

File tree

3 files changed

+104
-16
lines changed

3 files changed

+104
-16
lines changed

staging/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"k8s.io/apimachinery/pkg/labels"
2020
"k8s.io/apimachinery/pkg/types"
2121
"k8s.io/apimachinery/pkg/util/errors"
22+
"k8s.io/apimachinery/pkg/util/sets"
2223

2324
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
2425
"github.com/operator-framework/api/pkg/operators/v1alpha1"
@@ -1047,24 +1048,35 @@ func (a *Operator) ensureOpGroupClusterRole(op *operatorsv1.OperatorGroup, suffi
10471048
}
10481049

10491050
func (a *Operator) getClusterRoleAggregationRule(apis cache.APISet, suffix string) (*rbacv1.AggregationRule, error) {
1050-
var selectors []metav1.LabelSelector
1051+
if len(apis) == 0 {
1052+
return nil, nil
1053+
}
1054+
1055+
aggregationLabels := sets.New[string]()
10511056
for api := range apis {
10521057
aggregationLabel, err := aggregationLabelFromAPIKey(api, suffix)
10531058
if err != nil {
10541059
return nil, err
10551060
}
1061+
aggregationLabels.Insert(aggregationLabel)
1062+
}
1063+
1064+
// The order of the resulting selectors MUST BE deterministic in order
1065+
// to avoid unnecessary writes against the API server where only the order
1066+
// is changing. Therefore, we use `sets.List` to iterate. It returns a
1067+
// sorted slice of the aggregation labels.
1068+
selectors := make([]metav1.LabelSelector, 0, aggregationLabels.Len())
1069+
for _, aggregationLabel := range sets.List(aggregationLabels) {
10561070
selectors = append(selectors, metav1.LabelSelector{
10571071
MatchLabels: map[string]string{
10581072
aggregationLabel: "true",
10591073
},
10601074
})
10611075
}
1062-
if len(selectors) > 0 {
1063-
return &rbacv1.AggregationRule{
1064-
ClusterRoleSelectors: selectors,
1065-
}, nil
1066-
}
1067-
return nil, nil
1076+
1077+
return &rbacv1.AggregationRule{
1078+
ClusterRoleSelectors: selectors,
1079+
}, nil
10681080
}
10691081

10701082
func (a *Operator) ensureOpGroupClusterRoles(op *operatorsv1.OperatorGroup, apis cache.APISet) error {

staging/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup_test.go

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@ import (
88
"github.com/sirupsen/logrus/hooks/test"
99
"github.com/stretchr/testify/assert"
1010
"github.com/stretchr/testify/require"
11-
"k8s.io/client-go/metadata/metadatalister"
12-
11+
rbacv1 "k8s.io/api/rbac/v1"
1312
"k8s.io/apimachinery/pkg/api/errors"
1413
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1514
"k8s.io/apimachinery/pkg/labels"
15+
"k8s.io/client-go/metadata/metadatalister"
1616
ktesting "k8s.io/client-go/testing"
1717

1818
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1919
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake"
20+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache"
21+
"github.com/operator-framework/operator-registry/pkg/registry"
2022
)
2123

2224
func TestCopyToNamespace(t *testing.T) {
@@ -407,3 +409,65 @@ func TestCSVCopyPrototype(t *testing.T) {
407409
},
408410
}, dst)
409411
}
412+
413+
func TestOperator_getClusterRoleAggregationRule(t *testing.T) {
414+
tests := []struct {
415+
name string
416+
apis cache.APISet
417+
suffix string
418+
want func(*testing.T, *rbacv1.AggregationRule)
419+
wantErr require.ErrorAssertionFunc
420+
}{
421+
{
422+
name: "no aggregation rule when no APIs",
423+
apis: cache.APISet{},
424+
suffix: "admin",
425+
want: func(t *testing.T, rule *rbacv1.AggregationRule) {
426+
require.Nil(t, rule)
427+
},
428+
wantErr: require.NoError,
429+
},
430+
{
431+
name: "ordered selectors in aggregation rule",
432+
apis: cache.APISet{
433+
registry.APIKey{Group: "example.com", Version: "v1alpha1", Kind: "Foo"}: {},
434+
registry.APIKey{Group: "example.com", Version: "v1alpha2", Kind: "Foo"}: {},
435+
registry.APIKey{Group: "example.com", Version: "v1alpha3", Kind: "Foo"}: {},
436+
registry.APIKey{Group: "example.com", Version: "v1alpha4", Kind: "Foo"}: {},
437+
registry.APIKey{Group: "example.com", Version: "v1alpha5", Kind: "Foo"}: {},
438+
registry.APIKey{Group: "example.com", Version: "v1alpha1", Kind: "Bar"}: {},
439+
registry.APIKey{Group: "example.com", Version: "v1alpha2", Kind: "Bar"}: {},
440+
registry.APIKey{Group: "example.com", Version: "v1alpha3", Kind: "Bar"}: {},
441+
registry.APIKey{Group: "example.com", Version: "v1alpha4", Kind: "Bar"}: {},
442+
registry.APIKey{Group: "example.com", Version: "v1alpha5", Kind: "Bar"}: {},
443+
},
444+
suffix: "admin",
445+
want: func(t *testing.T, rule *rbacv1.AggregationRule) {
446+
getOneKey := func(t *testing.T, m map[string]string) string {
447+
require.Len(t, m, 1)
448+
for k := range m {
449+
return k
450+
}
451+
t.Fatalf("no keys found in map")
452+
return ""
453+
}
454+
455+
a := getOneKey(t, rule.ClusterRoleSelectors[0].MatchLabels)
456+
for _, selector := range rule.ClusterRoleSelectors[1:] {
457+
b := getOneKey(t, selector.MatchLabels)
458+
require.Lessf(t, a, b, "expected selector match labels keys to be in sorted ascending order")
459+
a = b
460+
}
461+
},
462+
wantErr: require.NoError,
463+
},
464+
}
465+
for _, tt := range tests {
466+
t.Run(tt.name, func(t *testing.T) {
467+
a := &Operator{}
468+
got, err := a.getClusterRoleAggregationRule(tt.apis, tt.suffix)
469+
tt.wantErr(t, err)
470+
tt.want(t, got)
471+
})
472+
}
473+
}

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup.go

Lines changed: 19 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)