@@ -25,6 +25,43 @@ const (
2525 operatorDeploymentName = "rhacs-operator-controller-manager"
2626)
2727
28+ // clusterServiceVersion represents the relevant structure of a ClusterServiceVersion YAML
29+ // that we need to patch for local image references. Uses inline maps to preserve
30+ // all fields not explicitly defined.
31+ type clusterServiceVersion struct {
32+ Spec struct {
33+ Install struct {
34+ Spec struct {
35+ Deployments []struct {
36+ Spec struct {
37+ Template struct {
38+ Spec struct {
39+ Containers []struct {
40+ Image string `yaml:"image"`
41+ Env []struct {
42+ Name string `yaml:"name"`
43+ Value string `yaml:"value"`
44+ ExtraFields map [string ]interface {} `yaml:",inline"`
45+ } `yaml:"env"`
46+ ExtraFields map [string ]interface {} `yaml:",inline"`
47+ } `yaml:"containers"`
48+ ExtraFields map [string ]interface {} `yaml:",inline"`
49+ } `yaml:"spec"`
50+ ExtraFields map [string ]interface {} `yaml:",inline"`
51+ } `yaml:"template"`
52+ ExtraFields map [string ]interface {} `yaml:",inline"`
53+ } `yaml:"spec"`
54+ ExtraFields map [string ]interface {} `yaml:",inline"`
55+ } `yaml:"deployments"`
56+ ExtraFields map [string ]interface {} `yaml:",inline"`
57+ } `yaml:"spec"`
58+ ExtraFields map [string ]interface {} `yaml:",inline"`
59+ } `yaml:"install"`
60+ ExtraFields map [string ]interface {} `yaml:",inline"`
61+ } `yaml:"spec"`
62+ ExtraFields map [string ]interface {} `yaml:",inline"`
63+ }
64+
2865// deployOperator deploys the RHACS operator
2966func (d * Deployer ) deployOperator (ctx context.Context ) error {
3067 d .logger .Infof ("Operator tag: %s" , d .operatorTag )
@@ -331,7 +368,7 @@ func (d *Deployer) deployOperatorFromCSV(ctx context.Context, bundleDir string)
331368 // Patch CSV with local image references if using local images
332369 if d .usingLocalImages {
333370 d .logger .Info ("Patching CSV with local image references" )
334- if err := patchCSVWithLocalImages (csvFile , d .mainImageTag , d .localImages ); err != nil {
371+ if err := patchCSVWithLocalImages (csvFile , d .mainImageTag , d .operatorTag , d . localImages ); err != nil {
335372 return fmt .Errorf ("failed to patch CSV with local images: %w" , err )
336373 }
337374 }
@@ -383,9 +420,7 @@ func (d *Deployer) deployOperatorFromCSV(ctx context.Context, bundleDir string)
383420// envVarToImageName converts a RELATED_IMAGE_* environment variable name to an image name.
384421// e.g., RELATED_IMAGE_SCANNER_V4_DB → scanner-v4-db
385422func envVarToImageName (envVar string ) string {
386- // Remove RELATED_IMAGE_ prefix
387423 name := strings .TrimPrefix (envVar , "RELATED_IMAGE_" )
388- // Convert to lowercase and replace underscores with hyphens
389424 name = strings .ToLower (name )
390425 name = strings .ReplaceAll (name , "_" , "-" )
391426 return name
@@ -398,86 +433,34 @@ func envVarToImageName(envVar string) string {
398433//
399434// The function only patches images that exist in the localImages map, leaving
400435// other image references unchanged.
401- func patchCSVWithLocalImages (csvFile , mainImageTag string , localImages map [string ]string ) error {
402- // Return early if no local images to patch
403- if len (localImages ) == 0 {
404- return nil
405- }
406-
436+ func patchCSVWithLocalImages (csvFile , mainImageTag , operatorTag string , localImages map [string ]string ) error {
407437 // Read the CSV file
408438 content , err := os .ReadFile (csvFile )
409439 if err != nil {
410440 return fmt .Errorf ("failed to read CSV file: %w" , err )
411441 }
412442
413- // Unmarshal to map[string]interface{}
414- var csvData map [string ]interface {}
443+ var csvData clusterServiceVersion
415444 if err := yaml .Unmarshal (content , & csvData ); err != nil {
416445 return fmt .Errorf ("failed to parse CSV YAML: %w" , err )
417446 }
418447
419- // Navigate to the container spec
420- spec , ok := csvData ["spec" ].(map [string ]interface {})
421- if ! ok {
422- return errors .New ("CSV missing 'spec' field" )
423- }
424-
425- install , ok := spec ["install" ].(map [string ]interface {})
426- if ! ok {
427- return errors .New ("CSV missing 'spec.install' field" )
428- }
429-
430- installSpec , ok := install ["spec" ].(map [string ]interface {})
431- if ! ok {
432- return errors .New ("CSV missing 'spec.install.spec' field" )
433- }
434-
435- deployments , ok := installSpec ["deployments" ].([]interface {})
436- if ! ok || len (deployments ) == 0 {
437- return errors .New ("CSV missing 'spec.install.spec.deployments' field or deployments array is empty" )
438- }
439-
440- deployment , ok := deployments [0 ].(map [string ]interface {})
441- if ! ok {
442- return errors .New ("invalid deployment structure in CSV" )
443- }
444-
445- deploymentSpec , ok := deployment ["spec" ].(map [string ]interface {})
446- if ! ok {
447- return errors .New ("CSV missing deployment spec" )
448+ // Validate structure
449+ if len (csvData .Spec .Install .Spec .Deployments ) == 0 {
450+ return errors .New ("CSV missing deployments" )
448451 }
449-
450- template , ok := deploymentSpec ["template" ].(map [string ]interface {})
451- if ! ok {
452- return errors .New ("CSV missing deployment template" )
453- }
454-
455- podSpec , ok := template ["spec" ].(map [string ]interface {})
456- if ! ok {
457- return errors .New ("CSV missing pod spec" )
458- }
459-
460- containers , ok := podSpec ["containers" ].([]interface {})
461- if ! ok || len (containers ) == 0 {
462- return errors .New ("CSV missing containers or containers array is empty" )
452+ if len (csvData .Spec .Install .Spec .Deployments [0 ].Spec .Template .Spec .Containers ) == 0 {
453+ return errors .New ("CSV missing containers" )
463454 }
464455
465- container , ok := containers [0 ].(map [string ]interface {})
466- if ! ok {
467- return errors .New ("invalid container structure in CSV" )
468- }
456+ // Get reference to the first container
457+ container := & csvData .Spec .Install .Spec .Deployments [0 ].Spec .Template .Spec .Containers [0 ]
469458
470459 // Patch operator image if it exists in localImages
471460 // Use the actual detected image reference to handle fallback branding cases
472- operatorImageKey := "stackrox-operator:" + mainImageTag
461+ operatorImageKey := "stackrox-operator:" + operatorTag
473462 if imageRef , ok := localImages [operatorImageKey ]; ok {
474- container ["image" ] = imageRef
475- }
476-
477- // Patch RELATED_IMAGE_* environment variables
478- envVars , ok := container ["env" ].([]interface {})
479- if ! ok {
480- return errors .New ("CSV missing container env variables" )
463+ container .Image = imageRef
481464 }
482465
483466 // Build a reverse map from image name to full image reference for quick lookup
@@ -491,25 +474,18 @@ func patchCSVWithLocalImages(csvFile, mainImageTag string, localImages map[strin
491474 }
492475 }
493476
494- for _ , envVar := range envVars {
495- envMap , ok := envVar .(map [string ]interface {})
496- if ! ok {
497- continue
498- }
499-
500- envName , ok := envMap ["name" ].(string )
501- if ! ok {
502- continue
503- }
477+ // Patch RELATED_IMAGE_* environment variables
478+ for i := range container .Env {
479+ envVar := & container .Env [i ]
504480
505481 // Check if this is a RELATED_IMAGE_* env var
506- if ! strings .HasPrefix (envName , "RELATED_IMAGE_" ) {
482+ if ! strings .HasPrefix (envVar . Name , "RELATED_IMAGE_" ) {
507483 continue
508484 }
509485
510486 // Convert RELATED_IMAGE_FOO_BAR to foo-bar
511487 // e.g., RELATED_IMAGE_SCANNER_V4_DB → scanner-v4-db
512- imageName := envVarToImageName (envName )
488+ imageName := envVarToImageName (envVar . Name )
513489
514490 // Special case: scanner-v4-indexer and scanner-v4-matcher both use the scanner-v4 image
515491 // The same image runs in different modes based on runtime configuration
@@ -520,7 +496,7 @@ func patchCSVWithLocalImages(csvFile, mainImageTag string, localImages map[strin
520496 // Check if we have this image locally and use the actual detected reference
521497 // This handles cases where an image was found at a fallback branding org
522498 if imageRef , found := imageNameToRef [imageName ]; found {
523- envMap [ "value" ] = imageRef
499+ envVar . Value = imageRef
524500 }
525501 }
526502
0 commit comments