Skip to content

Commit 0893dd4

Browse files
author
Per Goncalves da Silva
committed
move render package file to convert and replace converter with renderer
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent 29bdcaa commit 0893dd4

File tree

11 files changed

+296
-560
lines changed

11 files changed

+296
-560
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package convert
2+
3+
import (
4+
"cmp"
5+
"fmt"
6+
"strings"
7+
8+
corev1 "k8s.io/api/core/v1"
9+
rbacv1 "k8s.io/api/rbac/v1"
10+
"k8s.io/apimachinery/pkg/util/sets"
11+
"k8s.io/utils/ptr"
12+
"sigs.k8s.io/controller-runtime/pkg/client"
13+
14+
registrybundle "github.com/operator-framework/operator-registry/pkg/lib/bundle"
15+
16+
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util"
17+
)
18+
19+
type UniqueNameGenerator func(string, interface{}) (string, error)
20+
21+
type Options struct {
22+
InstallNamespace string
23+
TargetNamespaces []string
24+
UniqueNameGenerator UniqueNameGenerator
25+
}
26+
27+
type ResourceGenerator func(rv1 *RegistryV1, opts Options) ([]client.Object, error)
28+
29+
func (g ResourceGenerator) GenerateResources(rv1 *RegistryV1, opts Options) ([]client.Object, error) {
30+
return g(rv1, opts)
31+
}
32+
33+
func ChainedResourceGenerator(resourceGenerators ...ResourceGenerator) ResourceGenerator {
34+
return func(rv1 *RegistryV1, opts Options) ([]client.Object, error) {
35+
//nolint:prealloc
36+
var renderedObjects []client.Object
37+
for _, generator := range resourceGenerators {
38+
objs, err := generator(rv1, opts)
39+
if err != nil {
40+
return nil, err
41+
}
42+
renderedObjects = append(renderedObjects, objs...)
43+
}
44+
return renderedObjects, nil
45+
}
46+
}
47+
48+
func BundleDeploymentGenerator(rv1 *RegistryV1, opts Options) ([]client.Object, error) {
49+
objs := make([]client.Object, 0, len(rv1.CSV.Spec.InstallStrategy.StrategySpec.DeploymentSpecs))
50+
for _, depSpec := range rv1.CSV.Spec.InstallStrategy.StrategySpec.DeploymentSpecs {
51+
annotations := util.MergeMaps(rv1.CSV.Annotations, depSpec.Spec.Template.Annotations)
52+
annotations["olm.targetNamespaces"] = strings.Join(opts.TargetNamespaces, ",")
53+
depSpec.Spec.Template.Annotations = annotations
54+
55+
// Hardcode the deployment with RevisionHistoryLimit=1 (something OLMv0 does, not sure why)
56+
depSpec.Spec.RevisionHistoryLimit = ptr.To(int32(1))
57+
58+
objs = append(objs,
59+
GenerateDeploymentResource(
60+
depSpec.Name,
61+
opts.InstallNamespace,
62+
WithDeploymentSpec(depSpec.Spec),
63+
WithLabels(depSpec.Label),
64+
),
65+
)
66+
}
67+
return objs, nil
68+
}
69+
70+
func BundlePermissionsGenerator(rv1 *RegistryV1, opts Options) ([]client.Object, error) {
71+
permissions := rv1.CSV.Spec.InstallStrategy.StrategySpec.Permissions
72+
73+
// If we're in AllNamespaces mode permissions will be treated as clusterPermissions
74+
if len(opts.TargetNamespaces) == 1 && opts.TargetNamespaces[0] == "" {
75+
return nil, nil
76+
}
77+
78+
objs := make([]client.Object, 0, 2*len(opts.TargetNamespaces)*len(permissions))
79+
for _, ns := range opts.TargetNamespaces {
80+
for _, permission := range permissions {
81+
saName := saNameOrDefault(permission.ServiceAccountName)
82+
name, err := opts.UniqueNameGenerator(fmt.Sprintf("%s-%s", rv1.CSV.Name, saName), permission)
83+
if err != nil {
84+
return nil, err
85+
}
86+
87+
objs = append(objs,
88+
GenerateRoleResource(name, ns, WithRules(permission.Rules...)),
89+
GenerateRoleBindingResource(
90+
name,
91+
ns,
92+
WithSubjects(rbacv1.Subject{Kind: "ServiceAccount", Namespace: opts.InstallNamespace, Name: saName}),
93+
WithRoleRef(rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "Role", Name: name}),
94+
),
95+
)
96+
}
97+
}
98+
return objs, nil
99+
}
100+
101+
func BundleClusterPermissionsGenerator(rv1 *RegistryV1, opts Options) ([]client.Object, error) {
102+
clusterPermissions := rv1.CSV.Spec.InstallStrategy.StrategySpec.ClusterPermissions
103+
104+
// If we're in AllNamespaces mode, promote the permissions to clusterPermissions
105+
if len(opts.TargetNamespaces) == 1 && opts.TargetNamespaces[0] == "" {
106+
for _, p := range rv1.CSV.Spec.InstallStrategy.StrategySpec.Permissions {
107+
p.Rules = append(p.Rules, rbacv1.PolicyRule{
108+
Verbs: []string{"get", "list", "watch"},
109+
APIGroups: []string{corev1.GroupName},
110+
Resources: []string{"namespaces"},
111+
})
112+
clusterPermissions = append(clusterPermissions, p)
113+
}
114+
}
115+
116+
objs := make([]client.Object, 0, 2*len(clusterPermissions))
117+
for _, permission := range clusterPermissions {
118+
saName := saNameOrDefault(permission.ServiceAccountName)
119+
name, err := opts.UniqueNameGenerator(fmt.Sprintf("%s-%s", rv1.CSV.Name, saName), permission)
120+
if err != nil {
121+
return nil, err
122+
}
123+
objs = append(objs,
124+
GenerateClusterRoleResource(name, WithRules(permission.Rules...)),
125+
GenerateClusterRoleBindingResource(
126+
name,
127+
WithSubjects(rbacv1.Subject{Kind: "ServiceAccount", Namespace: opts.InstallNamespace, Name: saName}),
128+
WithRoleRef(rbacv1.RoleRef{APIGroup: rbacv1.GroupName, Kind: "ClusterRole", Name: name}),
129+
),
130+
)
131+
}
132+
return objs, nil
133+
}
134+
135+
func BundleServiceAccountGenerator(rv1 *RegistryV1, opts Options) ([]client.Object, error) {
136+
allPermissions := append(
137+
rv1.CSV.Spec.InstallStrategy.StrategySpec.Permissions,
138+
rv1.CSV.Spec.InstallStrategy.StrategySpec.ClusterPermissions...,
139+
)
140+
141+
serviceAccountNames := sets.Set[string]{}
142+
for _, permission := range allPermissions {
143+
serviceAccountNames.Insert(saNameOrDefault(permission.ServiceAccountName))
144+
}
145+
146+
objs := make([]client.Object, 0, len(serviceAccountNames))
147+
for _, serviceAccountName := range serviceAccountNames.UnsortedList() {
148+
// no need to generate the default service account
149+
if serviceAccountName != "default" {
150+
objs = append(objs, GenerateServiceAccountResource(serviceAccountName, opts.InstallNamespace))
151+
}
152+
}
153+
return objs, nil
154+
}
155+
156+
func BundleCRDGenerator(rv1 *RegistryV1, _ Options) ([]client.Object, error) {
157+
objs := make([]client.Object, 0, len(rv1.CRDs))
158+
for _, crd := range rv1.CRDs {
159+
objs = append(objs, crd.DeepCopy())
160+
}
161+
return objs, nil
162+
}
163+
164+
func BundleAdditionalResourcesGenerator(rv1 *RegistryV1, opts Options) ([]client.Object, error) {
165+
objs := make([]client.Object, 0, len(rv1.Others))
166+
for _, res := range rv1.Others {
167+
supported, namespaced := registrybundle.IsSupported(res.GetKind())
168+
if !supported {
169+
return nil, fmt.Errorf("bundle contains unsupported resource: Name: %v, Kind: %v", res.GetName(), res.GetKind())
170+
}
171+
172+
obj := res.DeepCopy()
173+
if namespaced {
174+
obj.SetNamespace(opts.InstallNamespace)
175+
}
176+
177+
objs = append(objs, obj)
178+
}
179+
return objs, nil
180+
}
181+
182+
func saNameOrDefault(saName string) string {
183+
return cmp.Or(saName, "default")
184+
}

internal/operator-controller/rukpak/convert/render/generate_test.go renamed to internal/operator-controller/rukpak/convert/generate_test.go

+24-25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package render_test
1+
package convert_test
22

33
import (
44
"cmp"
@@ -19,14 +19,13 @@ import (
1919
"github.com/operator-framework/api/pkg/operators/v1alpha1"
2020

2121
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/convert"
22-
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/convert/render"
2322
)
2423

2524
func Test_BundleDeploymentGenerator(t *testing.T) {
2625
for _, tc := range []struct {
2726
name string
2827
bundle *convert.RegistryV1
29-
opts render.Options
28+
opts convert.Options
3029
expectedResources []client.Object
3130
}{
3231
{
@@ -59,7 +58,7 @@ func Test_BundleDeploymentGenerator(t *testing.T) {
5958
),
6059
),
6160
},
62-
opts: render.Options{
61+
opts: convert.Options{
6362
InstallNamespace: "install-namespace",
6463
TargetNamespaces: []string{"watch-namespace-one", "watch-namespace-two"},
6564
},
@@ -114,7 +113,7 @@ func Test_BundleDeploymentGenerator(t *testing.T) {
114113
},
115114
} {
116115
t.Run(tc.name, func(t *testing.T) {
117-
objs, err := render.BundleDeploymentGenerator(tc.bundle, tc.opts)
116+
objs, err := convert.BundleDeploymentGenerator(tc.bundle, tc.opts)
118117
require.NoError(t, err)
119118
require.Equal(t, tc.expectedResources, objs)
120119
})
@@ -128,13 +127,13 @@ func Test_BundlePermissionsGenerator(t *testing.T) {
128127

129128
for _, tc := range []struct {
130129
name string
131-
opts render.Options
130+
opts convert.Options
132131
bundle *convert.RegistryV1
133132
expectedResources []client.Object
134133
}{
135134
{
136135
name: "does not generate any resources when in AllNamespaces mode (target namespace is [''])",
137-
opts: render.Options{
136+
opts: convert.Options{
138137
InstallNamespace: "install-namespace",
139138
TargetNamespaces: []string{""},
140139
UniqueNameGenerator: fakeUniqueNameGenerator,
@@ -160,7 +159,7 @@ func Test_BundlePermissionsGenerator(t *testing.T) {
160159
},
161160
{
162161
name: "generates role and rolebinding for permission service-account when in Single/OwnNamespace mode (target namespace contains a single namespace)",
163-
opts: render.Options{
162+
opts: convert.Options{
164163
InstallNamespace: "install-namespace",
165164
TargetNamespaces: []string{"watch-namespace"},
166165
UniqueNameGenerator: fakeUniqueNameGenerator,
@@ -235,7 +234,7 @@ func Test_BundlePermissionsGenerator(t *testing.T) {
235234
},
236235
{
237236
name: "generates role and rolebinding for permission service-account for each target namespace when in MultiNamespace install mode (target namespace contains multiple namespaces)",
238-
opts: render.Options{
237+
opts: convert.Options{
239238
InstallNamespace: "install-namespace",
240239
TargetNamespaces: []string{"watch-namespace", "watch-namespace-two"},
241240
UniqueNameGenerator: fakeUniqueNameGenerator,
@@ -354,7 +353,7 @@ func Test_BundlePermissionsGenerator(t *testing.T) {
354353
},
355354
{
356355
name: "generates role and rolebinding for each permission service-account",
357-
opts: render.Options{
356+
opts: convert.Options{
358357
InstallNamespace: "install-namespace",
359358
TargetNamespaces: []string{"watch-namespace"},
360359
UniqueNameGenerator: fakeUniqueNameGenerator,
@@ -471,7 +470,7 @@ func Test_BundlePermissionsGenerator(t *testing.T) {
471470
},
472471
{
473472
name: "treats empty service account as 'default' service account",
474-
opts: render.Options{
473+
opts: convert.Options{
475474
InstallNamespace: "install-namespace",
476475
TargetNamespaces: []string{"watch-namespace"},
477476
UniqueNameGenerator: fakeUniqueNameGenerator,
@@ -538,7 +537,7 @@ func Test_BundlePermissionsGenerator(t *testing.T) {
538537
},
539538
} {
540539
t.Run(tc.name, func(t *testing.T) {
541-
objs, err := render.BundlePermissionsGenerator(tc.bundle, tc.opts)
540+
objs, err := convert.BundlePermissionsGenerator(tc.bundle, tc.opts)
542541
require.NoError(t, err)
543542
for i := range objs {
544543
require.Equal(t, tc.expectedResources[i], objs[i], "failed to find expected resource at index %d", i)
@@ -555,13 +554,13 @@ func Test_BundleClusterPermissionsGenerator(t *testing.T) {
555554

556555
for _, tc := range []struct {
557556
name string
558-
opts render.Options
557+
opts convert.Options
559558
bundle *convert.RegistryV1
560559
expectedResources []client.Object
561560
}{
562561
{
563562
name: "promotes permissions to clusters permissions and adds namespace policy rule when in AllNamespaces mode (target namespace is [''])",
564-
opts: render.Options{
563+
opts: convert.Options{
565564
InstallNamespace: "install-namespace",
566565
TargetNamespaces: []string{""},
567566
UniqueNameGenerator: fakeUniqueNameGenerator,
@@ -682,7 +681,7 @@ func Test_BundleClusterPermissionsGenerator(t *testing.T) {
682681
},
683682
{
684683
name: "generates clusterroles and clusterrolebindings for clusterpermissions",
685-
opts: render.Options{
684+
opts: convert.Options{
686685
InstallNamespace: "install-namespace",
687686
TargetNamespaces: []string{"watch-namespace"},
688687
UniqueNameGenerator: fakeUniqueNameGenerator,
@@ -795,7 +794,7 @@ func Test_BundleClusterPermissionsGenerator(t *testing.T) {
795794
},
796795
{
797796
name: "treats empty service accounts as 'default' service account",
798-
opts: render.Options{
797+
opts: convert.Options{
799798
InstallNamespace: "install-namespace",
800799
TargetNamespaces: []string{"watch-namespace"},
801800
UniqueNameGenerator: fakeUniqueNameGenerator,
@@ -860,7 +859,7 @@ func Test_BundleClusterPermissionsGenerator(t *testing.T) {
860859
},
861860
} {
862861
t.Run(tc.name, func(t *testing.T) {
863-
objs, err := render.BundleClusterPermissionsGenerator(tc.bundle, tc.opts)
862+
objs, err := convert.BundleClusterPermissionsGenerator(tc.bundle, tc.opts)
864863
require.NoError(t, err)
865864
for i := range objs {
866865
require.Equal(t, tc.expectedResources[i], objs[i], "failed to find expected resource at index %d", i)
@@ -873,13 +872,13 @@ func Test_BundleClusterPermissionsGenerator(t *testing.T) {
873872
func Test_BundleServiceAccountGenerator(t *testing.T) {
874873
for _, tc := range []struct {
875874
name string
876-
opts render.Options
875+
opts convert.Options
877876
bundle *convert.RegistryV1
878877
expectedResources []client.Object
879878
}{
880879
{
881880
name: "generates unique set of clusterpermissions and permissions service accounts in the install namespace",
882-
opts: render.Options{
881+
opts: convert.Options{
883882
InstallNamespace: "install-namespace",
884883
},
885884
bundle: &convert.RegistryV1{
@@ -966,7 +965,7 @@ func Test_BundleServiceAccountGenerator(t *testing.T) {
966965
},
967966
{
968967
name: "treats empty service accounts as default and doesn't generate them",
969-
opts: render.Options{
968+
opts: convert.Options{
970969
InstallNamespace: "install-namespace",
971970
},
972971
bundle: &convert.RegistryV1{
@@ -1002,7 +1001,7 @@ func Test_BundleServiceAccountGenerator(t *testing.T) {
10021001
},
10031002
} {
10041003
t.Run(tc.name, func(t *testing.T) {
1005-
objs, err := render.BundleServiceAccountGenerator(tc.bundle, tc.opts)
1004+
objs, err := convert.BundleServiceAccountGenerator(tc.bundle, tc.opts)
10061005
require.NoError(t, err)
10071006
slices.SortFunc(objs, func(a, b client.Object) int {
10081007
return cmp.Compare(a.GetName(), b.GetName())
@@ -1016,7 +1015,7 @@ func Test_BundleServiceAccountGenerator(t *testing.T) {
10161015
}
10171016

10181017
func Test_BundleCRDGenerator_Succeeds(t *testing.T) {
1019-
opts := render.Options{
1018+
opts := convert.Options{
10201019
InstallNamespace: "install-namespace",
10211020
TargetNamespaces: []string{""},
10221021
}
@@ -1028,7 +1027,7 @@ func Test_BundleCRDGenerator_Succeeds(t *testing.T) {
10281027
},
10291028
}
10301029

1031-
objs, err := render.BundleCRDGenerator(bundle, opts)
1030+
objs, err := convert.BundleCRDGenerator(bundle, opts)
10321031
require.NoError(t, err)
10331032
require.Equal(t, []client.Object{
10341033
&apiextensionsv1.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: "crd-one"}},
@@ -1037,7 +1036,7 @@ func Test_BundleCRDGenerator_Succeeds(t *testing.T) {
10371036
}
10381037

10391038
func Test_BundleResourceGenerator_Succeeds(t *testing.T) {
1040-
opts := render.Options{
1039+
opts := convert.Options{
10411040
InstallNamespace: "install-namespace",
10421041
}
10431042

@@ -1068,7 +1067,7 @@ func Test_BundleResourceGenerator_Succeeds(t *testing.T) {
10681067
},
10691068
}
10701069

1071-
objs, err := render.BundleResourceGenerator(bundle, opts)
1070+
objs, err := convert.BundleAdditionalResourcesGenerator(bundle, opts)
10721071
require.NoError(t, err)
10731072
require.Len(t, objs, 2)
10741073
}

0 commit comments

Comments
 (0)