@@ -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