@@ -7,37 +7,25 @@ import (
77 "fmt"
88 "io/fs"
99 "path/filepath"
10- "strings"
1110
1211 "helm.sh/helm/v3/pkg/chart"
13- appsv1 "k8s.io/api/apps/v1"
14- corev1 "k8s.io/api/core/v1"
15- rbacv1 "k8s.io/api/rbac/v1"
1612 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
17- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1813 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1914 "k8s.io/apimachinery/pkg/runtime"
2015 "k8s.io/apimachinery/pkg/util/sets"
2116 "k8s.io/cli-runtime/pkg/resource"
22- "k8s.io/utils/ptr"
2317 "sigs.k8s.io/controller-runtime/pkg/client"
2418 "sigs.k8s.io/yaml"
2519
2620 "github.com/operator-framework/api/pkg/operators/v1alpha1"
2721 "github.com/operator-framework/operator-registry/alpha/property"
28- registrybundle "github.com/operator-framework/operator-registry/pkg/lib/bundle"
2922
3023 registry "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/operator-registry"
31- "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util"
24+ "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render"
25+ "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render/generators"
26+ "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/render/validators"
3227)
3328
34- type RegistryV1 struct {
35- PackageName string
36- CSV v1alpha1.ClusterServiceVersion
37- CRDs []apiextensionsv1.CustomResourceDefinition
38- Others []unstructured.Unstructured
39- }
40-
4129type Plain struct {
4230 Objects []client.Object
4331}
@@ -70,16 +58,16 @@ func RegistryV1ToHelmChart(rv1 fs.FS, installNamespace string, watchNamespace st
7058 return chrt , nil
7159}
7260
73- // ParseFS converts the rv1 filesystem into a RegistryV1.
61+ // ParseFS converts the rv1 filesystem into a render. RegistryV1.
7462// ParseFS expects the filesystem to conform to the registry+v1 format:
7563// metadata/annotations.yaml
7664// manifests/
7765// - csv.yaml
7866// - ...
7967//
8068// manifests directory does not contain subdirectories
81- func ParseFS (rv1 fs.FS ) (RegistryV1 , error ) {
82- reg := RegistryV1 {}
69+ func ParseFS (rv1 fs.FS ) (render. RegistryV1 , error ) {
70+ reg := render. RegistryV1 {}
8371 annotationsFileData , err := fs .ReadFile (rv1 , filepath .Join ("metadata" , "annotations.yaml" ))
8472 if err != nil {
8573 return reg , err
@@ -224,22 +212,23 @@ func validateTargetNamespaces(supportedInstallModes sets.Set[string], installNam
224212 return fmt .Errorf ("supported install modes %v do not support target namespaces %v" , sets.List [string ](supportedInstallModes ), targetNamespaces )
225213}
226214
227- func saNameOrDefault (saName string ) string {
228- if saName == "" {
229- return "default"
230- }
231- return saName
215+ var PlainConverter = Converter {
216+ BundleRenderer : render.BundleRenderer {
217+ BundleValidator : validators .RegistryV1BundleValidator ,
218+ ResourceGenerators : []render.ResourceGenerator {
219+ generators .BundleCSVRBACResourceGenerator .ResourceGenerator (),
220+ generators .BundleCRDGenerator ,
221+ generators .BundleAdditionalResourcesGenerator ,
222+ generators .BundleCSVDeploymentGenerator ,
223+ },
224+ },
232225}
233226
234227type Converter struct {
235- BundleValidator BundleValidator
228+ render. BundleRenderer
236229}
237230
238- func (c Converter ) Convert (rv1 RegistryV1 , installNamespace string , targetNamespaces []string ) (* Plain , error ) {
239- if err := c .BundleValidator .Validate (& rv1 ); err != nil {
240- return nil , err
241- }
242-
231+ func (c Converter ) Convert (rv1 render.RegistryV1 , installNamespace string , targetNamespaces []string ) (* Plain , error ) {
243232 if installNamespace == "" {
244233 installNamespace = rv1 .CSV .Annotations ["operatorframework.io/suggested-namespace" ]
245234 }
@@ -272,246 +261,9 @@ func (c Converter) Convert(rv1 RegistryV1, installNamespace string, targetNamesp
272261 return nil , fmt .Errorf ("webhookDefinitions are not supported" )
273262 }
274263
275- deployments := []appsv1.Deployment {}
276- serviceAccounts := map [string ]corev1.ServiceAccount {}
277- for _ , depSpec := range rv1 .CSV .Spec .InstallStrategy .StrategySpec .DeploymentSpecs {
278- annotations := util .MergeMaps (rv1 .CSV .Annotations , depSpec .Spec .Template .Annotations )
279- annotations ["olm.targetNamespaces" ] = strings .Join (targetNamespaces , "," )
280- depSpec .Spec .Template .Annotations = annotations
281-
282- // Hardcode the deployment with RevisionHistoryLimit=1 to replicate OLMv0 behavior
283- // https://github.com/operator-framework/operator-lifecycle-manager/blob/dfd0b2bea85038d3c0d65348bc812d297f16b8d2/pkg/controller/install/deployment.go#L181
284- depSpec .Spec .RevisionHistoryLimit = ptr .To (int32 (1 ))
285-
286- deployments = append (deployments , appsv1.Deployment {
287- TypeMeta : metav1.TypeMeta {
288- Kind : "Deployment" ,
289- APIVersion : appsv1 .SchemeGroupVersion .String (),
290- },
291-
292- ObjectMeta : metav1.ObjectMeta {
293- Namespace : installNamespace ,
294- Name : depSpec .Name ,
295- Labels : depSpec .Label ,
296- },
297- Spec : depSpec .Spec ,
298- })
299- saName := saNameOrDefault (depSpec .Spec .Template .Spec .ServiceAccountName )
300- serviceAccounts [saName ] = newServiceAccount (installNamespace , saName )
301- }
302-
303- // NOTES:
304- // 1. There's an extra Role for OperatorConditions: get/update/patch; resourceName=csv.name
305- // - This is managed by the OperatorConditions controller here: https://github.com/operator-framework/operator-lifecycle-manager/blob/9ced412f3e263b8827680dc0ad3477327cd9a508/pkg/controller/operators/operatorcondition_controller.go#L106-L109
306- // 2. There's an extra RoleBinding for the above mentioned role.
307- // - Every SA mentioned in the OperatorCondition.spec.serviceAccounts is a subject for this role binding: https://github.com/operator-framework/operator-lifecycle-manager/blob/9ced412f3e263b8827680dc0ad3477327cd9a508/pkg/controller/operators/operatorcondition_controller.go#L171-L177
308- // 3. strategySpec.permissions are _also_ given a clusterrole/clusterrole binding.
309- // - (for AllNamespaces mode only?)
310- // - (where does the extra namespaces get/list/watch rule come from?)
311-
312- roles := []rbacv1.Role {}
313- roleBindings := []rbacv1.RoleBinding {}
314- clusterRoles := []rbacv1.ClusterRole {}
315- clusterRoleBindings := []rbacv1.ClusterRoleBinding {}
316-
317- permissions := rv1 .CSV .Spec .InstallStrategy .StrategySpec .Permissions
318- clusterPermissions := rv1 .CSV .Spec .InstallStrategy .StrategySpec .ClusterPermissions
319- allPermissions := append (permissions , clusterPermissions ... )
320-
321- // Create all the service accounts
322- for _ , permission := range allPermissions {
323- saName := saNameOrDefault (permission .ServiceAccountName )
324- if _ , ok := serviceAccounts [saName ]; ! ok {
325- serviceAccounts [saName ] = newServiceAccount (installNamespace , saName )
326- }
327- }
328-
329- // If we're in AllNamespaces mode, promote the permissions to clusterPermissions
330- if len (targetNamespaces ) == 1 && targetNamespaces [0 ] == "" {
331- for _ , p := range permissions {
332- p .Rules = append (p .Rules , rbacv1.PolicyRule {
333- Verbs : []string {"get" , "list" , "watch" },
334- APIGroups : []string {corev1 .GroupName },
335- Resources : []string {"namespaces" },
336- })
337- clusterPermissions = append (clusterPermissions , p )
338- }
339- permissions = nil
340- }
341-
342- for _ , ns := range targetNamespaces {
343- for _ , permission := range permissions {
344- saName := saNameOrDefault (permission .ServiceAccountName )
345- name , err := generateName (fmt .Sprintf ("%s-%s" , rv1 .CSV .Name , saName ), permission )
346- if err != nil {
347- return nil , err
348- }
349- roles = append (roles , newRole (ns , name , permission .Rules ))
350- roleBindings = append (roleBindings , newRoleBinding (ns , name , name , installNamespace , saName ))
351- }
352- }
353-
354- for _ , permission := range clusterPermissions {
355- saName := saNameOrDefault (permission .ServiceAccountName )
356- name , err := generateName (fmt .Sprintf ("%s-%s" , rv1 .CSV .Name , saName ), permission )
357- if err != nil {
358- return nil , err
359- }
360- clusterRoles = append (clusterRoles , newClusterRole (name , permission .Rules ))
361- clusterRoleBindings = append (clusterRoleBindings , newClusterRoleBinding (name , name , installNamespace , saName ))
362- }
363-
364- objs := []client.Object {}
365- for _ , obj := range serviceAccounts {
366- obj := obj
367- if obj .GetName () != "default" {
368- objs = append (objs , & obj )
369- }
370- }
371- for _ , obj := range roles {
372- obj := obj
373- objs = append (objs , & obj )
374- }
375- for _ , obj := range roleBindings {
376- obj := obj
377- objs = append (objs , & obj )
378- }
379- for _ , obj := range clusterRoles {
380- obj := obj
381- objs = append (objs , & obj )
382- }
383- for _ , obj := range clusterRoleBindings {
384- obj := obj
385- objs = append (objs , & obj )
386- }
387- for _ , obj := range rv1 .CRDs {
388- objs = append (objs , & obj )
389- }
390- for _ , obj := range rv1 .Others {
391- obj := obj
392- supported , namespaced := registrybundle .IsSupported (obj .GetKind ())
393- if ! supported {
394- return nil , fmt .Errorf ("bundle contains unsupported resource: Name: %v, Kind: %v" , obj .GetName (), obj .GetKind ())
395- }
396- if namespaced {
397- obj .SetNamespace (installNamespace )
398- }
399- objs = append (objs , & obj )
400- }
401- for _ , obj := range deployments {
402- obj := obj
403- objs = append (objs , & obj )
404- }
405- return & Plain {Objects : objs }, nil
406- }
407-
408- var PlainConverter = Converter {
409- BundleValidator : RegistryV1BundleValidator ,
410- }
411-
412- const maxNameLength = 63
413-
414- func generateName (base string , o interface {}) (string , error ) {
415- hashStr , err := util .DeepHashObject (o )
264+ objs , err := c .BundleRenderer .Render (rv1 , installNamespace , targetNamespaces )
416265 if err != nil {
417- return "" , err
418- }
419- if len (base )+ len (hashStr ) > maxNameLength {
420- base = base [:maxNameLength - len (hashStr )- 1 ]
421- }
422-
423- return fmt .Sprintf ("%s-%s" , base , hashStr ), nil
424- }
425-
426- func newServiceAccount (namespace , name string ) corev1.ServiceAccount {
427- return corev1.ServiceAccount {
428- TypeMeta : metav1.TypeMeta {
429- Kind : "ServiceAccount" ,
430- APIVersion : corev1 .SchemeGroupVersion .String (),
431- },
432- ObjectMeta : metav1.ObjectMeta {
433- Namespace : namespace ,
434- Name : name ,
435- },
436- }
437- }
438-
439- func newRole (namespace , name string , rules []rbacv1.PolicyRule ) rbacv1.Role {
440- return rbacv1.Role {
441- TypeMeta : metav1.TypeMeta {
442- Kind : "Role" ,
443- APIVersion : rbacv1 .SchemeGroupVersion .String (),
444- },
445- ObjectMeta : metav1.ObjectMeta {
446- Namespace : namespace ,
447- Name : name ,
448- },
449- Rules : rules ,
450- }
451- }
452-
453- func newClusterRole (name string , rules []rbacv1.PolicyRule ) rbacv1.ClusterRole {
454- return rbacv1.ClusterRole {
455- TypeMeta : metav1.TypeMeta {
456- Kind : "ClusterRole" ,
457- APIVersion : rbacv1 .SchemeGroupVersion .String (),
458- },
459- ObjectMeta : metav1.ObjectMeta {
460- Name : name ,
461- },
462- Rules : rules ,
463- }
464- }
465-
466- func newRoleBinding (namespace , name , roleName , saNamespace string , saNames ... string ) rbacv1.RoleBinding {
467- subjects := make ([]rbacv1.Subject , 0 , len (saNames ))
468- for _ , saName := range saNames {
469- subjects = append (subjects , rbacv1.Subject {
470- Kind : "ServiceAccount" ,
471- Namespace : saNamespace ,
472- Name : saName ,
473- })
474- }
475- return rbacv1.RoleBinding {
476- TypeMeta : metav1.TypeMeta {
477- Kind : "RoleBinding" ,
478- APIVersion : rbacv1 .SchemeGroupVersion .String (),
479- },
480- ObjectMeta : metav1.ObjectMeta {
481- Namespace : namespace ,
482- Name : name ,
483- },
484- Subjects : subjects ,
485- RoleRef : rbacv1.RoleRef {
486- APIGroup : rbacv1 .GroupName ,
487- Kind : "Role" ,
488- Name : roleName ,
489- },
490- }
491- }
492-
493- func newClusterRoleBinding (name , roleName , saNamespace string , saNames ... string ) rbacv1.ClusterRoleBinding {
494- subjects := make ([]rbacv1.Subject , 0 , len (saNames ))
495- for _ , saName := range saNames {
496- subjects = append (subjects , rbacv1.Subject {
497- Kind : "ServiceAccount" ,
498- Namespace : saNamespace ,
499- Name : saName ,
500- })
501- }
502- return rbacv1.ClusterRoleBinding {
503- TypeMeta : metav1.TypeMeta {
504- Kind : "ClusterRoleBinding" ,
505- APIVersion : rbacv1 .SchemeGroupVersion .String (),
506- },
507- ObjectMeta : metav1.ObjectMeta {
508- Name : name ,
509- },
510- Subjects : subjects ,
511- RoleRef : rbacv1.RoleRef {
512- APIGroup : rbacv1 .GroupName ,
513- Kind : "ClusterRole" ,
514- Name : roleName ,
515- },
266+ return nil , err
516267 }
268+ return & Plain {Objects : objs }, nil
517269}
0 commit comments