Skip to content

Commit e48ec38

Browse files
Merge pull request #1102 from perdasilva/cherry-pick-1025-to-release-4.16-rebased
[release-4.16] OCPBUGS-58881: operatorgroup: ensure clusterroleselectors in clusterrole aggregation rules are sorted
2 parents e0b9b95 + 25d15cb commit e48ec38

File tree

3 files changed

+103
-14
lines changed

3 files changed

+103
-14
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"
@@ -1048,24 +1049,35 @@ func (a *Operator) ensureOpGroupClusterRole(op *operatorsv1.OperatorGroup, suffi
10481049
}
10491050

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

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

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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/stretchr/testify/assert"
99
"github.com/stretchr/testify/require"
1010

11+
rbacv1 "k8s.io/api/rbac/v1"
1112
"k8s.io/apimachinery/pkg/api/errors"
1213
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1314
"k8s.io/apimachinery/pkg/labels"
@@ -16,7 +17,9 @@ import (
1617
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1718
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake"
1819
listersv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1"
20+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache"
1921
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister/operatorlisterfakes"
22+
"github.com/operator-framework/operator-registry/pkg/registry"
2023
)
2124

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

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)