Skip to content

Commit 4abb4e7

Browse files
Per Goncalves da Silvaperdasilva
Per Goncalves da Silva
authored andcommitted
Annotated generated manifests
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent dd2703b commit 4abb4e7

File tree

3 files changed

+176
-110
lines changed

3 files changed

+176
-110
lines changed
+112-103
Original file line numberDiff line numberDiff line change
@@ -1,159 +1,168 @@
11
package convert
22

33
import (
4-
"fmt"
5-
"github.com/operator-framework/operator-controller/internal/shared/util/filter"
6-
rbacv1 "k8s.io/api/rbac/v1"
7-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
8-
"k8s.io/apimachinery/pkg/runtime/schema"
9-
"k8s.io/utils/ptr"
10-
"sigs.k8s.io/controller-runtime/pkg/client"
11-
"slices"
12-
"strings"
4+
"fmt"
5+
"slices"
6+
"strings"
7+
8+
rbacv1 "k8s.io/api/rbac/v1"
9+
"k8s.io/apimachinery/pkg/runtime/schema"
10+
"k8s.io/utils/ptr"
11+
"sigs.k8s.io/controller-runtime/pkg/client"
12+
13+
slicesutil "github.com/operator-framework/operator-controller/internal/shared/util/filter"
1314
)
1415

1516
var (
16-
unnamedResourceVerbs = []string{"create", "list", "watch"}
17-
namedResourceVerbs = []string{"get", "update", "patch", "delete"}
18-
19-
// clusterScopedResources is a slice of registry+v1 bundle supported cluster scoped resource kinds
20-
clusterScopedResources = []string{
21-
"ClusterRole",
22-
"ClusterRoleBinding",
23-
"PriorityClass",
24-
"ConsoleYAMLSample",
25-
"ConsoleQuickStart",
26-
"ConsoleCLIDownload",
27-
"ConsoleLink",
28-
"CustomResourceDefinition",
29-
}
30-
31-
// clusterScopedResources is a slice of registry+v1 bundle supported namespace scoped resource kinds
32-
namespaceScopedResources = []string{
33-
"Secret",
34-
"ConfigMap",
35-
"ServiceAccount",
36-
"Service",
37-
"Role",
38-
"RoleBinding",
39-
"PrometheusRule",
40-
"ServiceMonitor",
41-
"PodDisruptionBudget",
42-
"VerticalPodAutoscaler",
43-
"Deployment",
44-
}
17+
unnamedResourceVerbs = []string{"create", "list", "watch"}
18+
namedResourceVerbs = []string{"get", "update", "patch", "delete"}
19+
20+
// clusterScopedResources is a slice of registry+v1 bundle supported cluster scoped resource kinds
21+
clusterScopedResources = []string{
22+
"ClusterRole",
23+
"ClusterRoleBinding",
24+
"PriorityClass",
25+
"ConsoleYAMLSample",
26+
"ConsoleQuickStart",
27+
"ConsoleCLIDownload",
28+
"ConsoleLink",
29+
"CustomResourceDefinition",
30+
}
31+
32+
// clusterScopedResources is a slice of registry+v1 bundle supported namespace scoped resource kinds
33+
namespaceScopedResources = []string{
34+
"Secret",
35+
"ConfigMap",
36+
"ServiceAccount",
37+
"Service",
38+
"Role",
39+
"RoleBinding",
40+
"PrometheusRule",
41+
"ServiceMonitor",
42+
"PodDisruptionBudget",
43+
"VerticalPodAutoscaler",
44+
"Deployment",
45+
}
4546
)
4647

4748
// GenerateResourceManagerClusterRole generates a ClusterRole with permissions to manage objs resources. The
4849
// permissions also aggregate any permissions from any ClusterRoles in objs allowing the holder to also assign
49-
// the RBAC therein to another service account. Note: currently assumes objs have been created by convert.Convert.
50+
// the RBAC therein to another service account. Note: assumes objs have been created by convert.Convert.
51+
// The returned ClusterRole will not have set .metadata.name
5052
func GenerateResourceManagerClusterRole(objs []client.Object) *rbacv1.ClusterRole {
51-
return ptr.To(newClusterRole(
52-
"",
53-
slices.Concat(
54-
// cluster scoped resource creation and management rules
55-
generatePolicyRules(filter.Filter(objs, isClusterScopedResource)),
56-
// controller rbac scope
57-
collectRBACResourcePolicyRules(filter.Filter(objs, filter.And(isGeneratedResource, isOfKind("ClusterRole")))),
58-
),
59-
))
53+
rules := slices.Concat(
54+
// cluster scoped resource creation and management rules
55+
generatePolicyRules(slicesutil.Filter(objs, isClusterScopedResource)),
56+
// controller rbac scope
57+
collectRBACResourcePolicyRules(slicesutil.Filter(objs, slicesutil.And(isGeneratedResource, isOfKind("ClusterRole")))),
58+
)
59+
if len(rules) == 0 {
60+
return nil
61+
}
62+
return ptr.To(newClusterRole("", rules))
6063
}
6164

6265
// GenerateClusterExtensionFinalizerPolicyRule generates a policy rule that allows the holder to update
6366
// finalizer for a ClusterExtension with clusterExtensionName.
6467
func GenerateClusterExtensionFinalizerPolicyRule(clusterExtensionName string) rbacv1.PolicyRule {
65-
return rbacv1.PolicyRule{
66-
APIGroups: []string{"olm.operatorframework.io"},
67-
Resources: []string{"clusterextensions/finalizers"},
68-
Verbs: []string{"update"},
69-
ResourceNames: []string{clusterExtensionName},
70-
}
68+
return rbacv1.PolicyRule{
69+
APIGroups: []string{"olm.operatorframework.io"},
70+
Resources: []string{"clusterextensions/finalizers"},
71+
Verbs: []string{"update"},
72+
ResourceNames: []string{clusterExtensionName},
73+
}
7174
}
7275

7376
// GenerateResourceManagerRoles generates one or more Roles with permissions to manage objs resources in their
7477
// namespaces. The permissions also include any permissions defined in any Roles in objs within the namespace, allowing
7578
// the holder to also assign the RBAC therein to another service account.
7679
// Note: currently assumes objs have been created by convert.Convert.
80+
// The returned Roles will not have set .metadata.name
7781
func GenerateResourceManagerRoles(objs []client.Object) []*rbacv1.Role {
78-
return mapToSlice(filter.GroupBy(objs, namespaceName), generateRole)
82+
return mapToSlice(slicesutil.GroupBy(slicesutil.Filter(objs, isNamespaceScopedResource), namespaceName), generateRole)
7983
}
8084

8185
func generateRole(namespace string, namespaceObjs []client.Object) *rbacv1.Role {
82-
return ptr.To(newRole(
83-
namespace,
84-
"",
85-
slices.Concat(
86-
// namespace scoped resource creation and management rules
87-
generatePolicyRules(namespaceObjs),
88-
// controller rbac scope
89-
collectRBACResourcePolicyRules(filter.Filter(namespaceObjs, filter.And(isOfKind("Role"), isGeneratedResource))),
90-
),
91-
))
86+
return ptr.To(newRole(
87+
namespace,
88+
"",
89+
slices.Concat(
90+
// namespace scoped resource creation and management rules
91+
generatePolicyRules(namespaceObjs),
92+
// controller rbac scope
93+
collectRBACResourcePolicyRules(slicesutil.Filter(namespaceObjs, slicesutil.And(isOfKind("Role"), isGeneratedResource))),
94+
),
95+
))
9296
}
9397

9498
func generatePolicyRules(objs []client.Object) []rbacv1.PolicyRule {
95-
return slices.Concat(mapToSlice(filter.GroupBy(objs, groupKind), func(gk schema.GroupKind, resources []client.Object) []rbacv1.PolicyRule {
96-
return []rbacv1.PolicyRule{
97-
newPolicyRule(gk, unnamedResourceVerbs),
98-
newPolicyRule(gk, namedResourceVerbs, filter.Map(resources, toResourceName)...),
99-
}
100-
})...)
99+
return slices.Concat(
100+
mapToSlice(slicesutil.GroupBy(objs, groupKind), func(gk schema.GroupKind, resources []client.Object) []rbacv1.PolicyRule {
101+
return []rbacv1.PolicyRule{
102+
newPolicyRule(gk, unnamedResourceVerbs),
103+
newPolicyRule(gk, namedResourceVerbs, slicesutil.Map(resources, toResourceName)...),
104+
}
105+
})...,
106+
)
101107
}
102108

103109
func collectRBACResourcePolicyRules(objs []client.Object) []rbacv1.PolicyRule {
104-
return slices.Concat(filter.Map(objs, func(obj client.Object) []rbacv1.PolicyRule {
105-
if cr, ok := obj.(*rbacv1.ClusterRole); ok {
106-
return cr.Rules
107-
} else if r, ok := obj.(*rbacv1.Role); ok {
108-
return r.Rules
109-
} else {
110-
panic(fmt.Sprintf("unexpected type %T", obj))
111-
}
112-
})...)
110+
return slices.Concat(slicesutil.Map(objs, func(obj client.Object) []rbacv1.PolicyRule {
111+
if cr, ok := obj.(*rbacv1.ClusterRole); ok {
112+
return cr.Rules
113+
} else if r, ok := obj.(*rbacv1.Role); ok {
114+
return r.Rules
115+
} else {
116+
panic(fmt.Sprintf("unexpected type %T", obj))
117+
}
118+
})...)
113119
}
114120

115121
func newPolicyRule(groupKind schema.GroupKind, verbs []string, resourceNames ...string) rbacv1.PolicyRule {
116-
return rbacv1.PolicyRule{
117-
APIGroups: []string{groupKind.Group},
118-
Resources: []string{fmt.Sprintf("%ss", strings.ToLower(groupKind.Kind))},
119-
Verbs: verbs,
120-
ResourceNames: resourceNames,
121-
}
122+
return rbacv1.PolicyRule{
123+
APIGroups: []string{groupKind.Group},
124+
Resources: []string{fmt.Sprintf("%ss", strings.ToLower(groupKind.Kind))},
125+
Verbs: verbs,
126+
ResourceNames: resourceNames,
127+
}
122128
}
123129

124130
func mapToSlice[K comparable, V any, R any](m map[K]V, fn func(k K, v V) R) []R {
125-
out := make([]R, 0, len(m))
126-
for k, v := range m {
127-
out = append(out, fn(k, v))
128-
}
129-
return out
131+
out := make([]R, 0, len(m))
132+
for k, v := range m {
133+
out = append(out, fn(k, v))
134+
}
135+
return out
130136
}
131137

132138
func isClusterScopedResource(o client.Object) bool {
133-
return slices.Contains(clusterScopedResources, o.GetObjectKind().GroupVersionKind().Kind)
139+
return slices.Contains(clusterScopedResources, o.GetObjectKind().GroupVersionKind().Kind)
140+
}
141+
142+
func isNamespaceScopedResource(o client.Object) bool {
143+
return slices.Contains(namespaceScopedResources, o.GetObjectKind().GroupVersionKind().Kind)
134144
}
135145

136-
func isOfKind(kind string) filter.Predicate[client.Object] {
137-
return func(o client.Object) bool {
138-
return o.GetObjectKind().GroupVersionKind().Kind == kind
139-
}
146+
func isOfKind(kind string) slicesutil.Predicate[client.Object] {
147+
return func(o client.Object) bool {
148+
return o.GetObjectKind().GroupVersionKind().Kind == kind
149+
}
140150
}
141151

142152
func isGeneratedResource(o client.Object) bool {
143-
// TODO: this is a hack that abuses an internal implementation detail
144-
// we should probably annotate the generated resources coming out of convert.Convert
145-
_, ok := o.(*unstructured.Unstructured)
146-
return ok
153+
annotations := o.GetAnnotations()
154+
_, ok := annotations[AnnotationRegistryV1GeneratedManifest]
155+
return ok
147156
}
148157

149158
func groupKind(obj client.Object) schema.GroupKind {
150-
return obj.GetObjectKind().GroupVersionKind().GroupKind()
159+
return obj.GetObjectKind().GroupVersionKind().GroupKind()
151160
}
152161

153162
func namespaceName(obj client.Object) string {
154-
return obj.GetNamespace()
163+
return obj.GetNamespace()
155164
}
156165

157166
func toResourceName(o client.Object) string {
158-
return o.GetName()
167+
return o.GetName()
159168
}

Diff for: internal/operator-controller/rukpak/convert/registryv1.go

+22-6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ import (
2929
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util"
3030
)
3131

32+
const (
33+
AnnotationRegistryV1GeneratedManifest = "io.operatorframework.olm.generated-manifest"
34+
)
35+
3236
type RegistryV1 struct {
3337
PackageName string
3438
CSV v1alpha1.ClusterServiceVersion
@@ -255,7 +259,7 @@ func Convert(in RegistryV1, installNamespace string, targetNamespaces []string)
255259
return nil, fmt.Errorf("webhookDefinitions are not supported")
256260
}
257261

258-
deployments := []appsv1.Deployment{}
262+
deployments := make([]appsv1.Deployment, 0, len(in.CSV.Spec.InstallStrategy.StrategySpec.DeploymentSpecs))
259263
serviceAccounts := map[string]corev1.ServiceAccount{}
260264
for _, depSpec := range in.CSV.Spec.InstallStrategy.StrategySpec.DeploymentSpecs {
261265
annotations := util.MergeMaps(in.CSV.Annotations, depSpec.Spec.Template.Annotations)
@@ -340,23 +344,24 @@ func Convert(in RegistryV1, installNamespace string, targetNamespaces []string)
340344
}
341345

342346
objs := []client.Object{}
347+
343348
for _, obj := range serviceAccounts {
344349
obj := obj
345350
if obj.GetName() != "default" {
346-
objs = append(objs, &obj)
351+
objs = append(objs, annotateGenerated(&obj))
347352
}
348353
}
349354
for _, obj := range roles {
350355
obj := obj
351-
objs = append(objs, &obj)
356+
objs = append(objs, annotateGenerated(&obj))
352357
}
353358
for _, obj := range roleBindings {
354359
obj := obj
355-
objs = append(objs, &obj)
360+
objs = append(objs, annotateGenerated(&obj))
356361
}
357362
for _, obj := range clusterRoles {
358363
obj := obj
359-
objs = append(objs, &obj)
364+
objs = append(objs, annotateGenerated(&obj))
360365
}
361366
for _, obj := range clusterRoleBindings {
362367
obj := obj
@@ -375,13 +380,24 @@ func Convert(in RegistryV1, installNamespace string, targetNamespaces []string)
375380
}
376381
for _, obj := range deployments {
377382
obj := obj
378-
objs = append(objs, &obj)
383+
objs = append(objs, annotateGenerated(&obj))
379384
}
385+
380386
return &Plain{Objects: objs}, nil
381387
}
382388

383389
const maxNameLength = 63
384390

391+
func annotateGenerated(o client.Object) client.Object {
392+
annotations := o.GetAnnotations()
393+
if annotations == nil {
394+
annotations = make(map[string]string)
395+
}
396+
annotations[AnnotationRegistryV1GeneratedManifest] = ""
397+
o.SetAnnotations(annotations)
398+
return o
399+
}
400+
385401
func generateName(base string, o interface{}) (string, error) {
386402
hashStr, err := util.DeepHashObject(o)
387403
if err != nil {

0 commit comments

Comments
 (0)