@@ -24,6 +24,43 @@ const (
2424 operatorDeploymentName = "rhacs-operator-controller-manager"
2525)
2626
27+ // clusterServiceVersion represents the relevant structure of a ClusterServiceVersion YAML
28+ // that we need to patch for local image references. Uses inline maps to preserve
29+ // all fields not explicitly defined.
30+ type clusterServiceVersion struct {
31+ Spec struct {
32+ Install struct {
33+ Spec struct {
34+ Deployments []struct {
35+ Spec struct {
36+ Template struct {
37+ Spec struct {
38+ Containers []struct {
39+ Image string `yaml:"image"`
40+ Env []struct {
41+ Name string `yaml:"name"`
42+ Value string `yaml:"value"`
43+ ExtraFields map [string ]interface {} `yaml:",inline"`
44+ } `yaml:"env"`
45+ ExtraFields map [string ]interface {} `yaml:",inline"`
46+ } `yaml:"containers"`
47+ ExtraFields map [string ]interface {} `yaml:",inline"`
48+ } `yaml:"spec"`
49+ ExtraFields map [string ]interface {} `yaml:",inline"`
50+ } `yaml:"template"`
51+ ExtraFields map [string ]interface {} `yaml:",inline"`
52+ } `yaml:"spec"`
53+ ExtraFields map [string ]interface {} `yaml:",inline"`
54+ } `yaml:"deployments"`
55+ ExtraFields map [string ]interface {} `yaml:",inline"`
56+ } `yaml:"spec"`
57+ ExtraFields map [string ]interface {} `yaml:",inline"`
58+ } `yaml:"install"`
59+ ExtraFields map [string ]interface {} `yaml:",inline"`
60+ } `yaml:"spec"`
61+ ExtraFields map [string ]interface {} `yaml:",inline"`
62+ }
63+
2764// deployOperator deploys the RHACS operator
2865func (d * Deployer ) deployOperator (ctx context.Context ) error {
2966 d .logger .Infof ("Operator tag: %s" , d .operatorTag )
@@ -223,7 +260,7 @@ func (d *Deployer) deployOperatorFromCSV(ctx context.Context, bundleDir string)
223260 // Patch CSV with local image references if using local images
224261 if d .usingLocalImages {
225262 d .logger .Info ("Patching CSV with local image references" )
226- if err := patchCSVWithLocalImages (csvFile , d .mainImageTag , d .localImages ); err != nil {
263+ if err := patchCSVWithLocalImages (csvFile , d .mainImageTag , d .operatorTag , d . localImages ); err != nil {
227264 return fmt .Errorf ("failed to patch CSV with local images: %w" , err )
228265 }
229266 }
@@ -275,9 +312,7 @@ func (d *Deployer) deployOperatorFromCSV(ctx context.Context, bundleDir string)
275312// envVarToImageName converts a RELATED_IMAGE_* environment variable name to an image name.
276313// e.g., RELATED_IMAGE_SCANNER_V4_DB → scanner-v4-db
277314func envVarToImageName (envVar string ) string {
278- // Remove RELATED_IMAGE_ prefix
279315 name := strings .TrimPrefix (envVar , "RELATED_IMAGE_" )
280- // Convert to lowercase and replace underscores with hyphens
281316 name = strings .ToLower (name )
282317 name = strings .ReplaceAll (name , "_" , "-" )
283318 return name
@@ -290,86 +325,34 @@ func envVarToImageName(envVar string) string {
290325//
291326// The function only patches images that exist in the localImages map, leaving
292327// other image references unchanged.
293- func patchCSVWithLocalImages (csvFile , mainImageTag string , localImages map [string ]string ) error {
294- // Return early if no local images to patch
295- if len (localImages ) == 0 {
296- return nil
297- }
298-
328+ func patchCSVWithLocalImages (csvFile , mainImageTag , operatorTag string , localImages map [string ]string ) error {
299329 // Read the CSV file
300330 content , err := os .ReadFile (csvFile )
301331 if err != nil {
302332 return fmt .Errorf ("failed to read CSV file: %w" , err )
303333 }
304334
305- // Unmarshal to map[string]interface{}
306- var csvData map [string ]interface {}
335+ var csvData clusterServiceVersion
307336 if err := yaml .Unmarshal (content , & csvData ); err != nil {
308337 return fmt .Errorf ("failed to parse CSV YAML: %w" , err )
309338 }
310339
311- // Navigate to the container spec
312- spec , ok := csvData ["spec" ].(map [string ]interface {})
313- if ! ok {
314- return errors .New ("CSV missing 'spec' field" )
315- }
316-
317- install , ok := spec ["install" ].(map [string ]interface {})
318- if ! ok {
319- return errors .New ("CSV missing 'spec.install' field" )
320- }
321-
322- installSpec , ok := install ["spec" ].(map [string ]interface {})
323- if ! ok {
324- return errors .New ("CSV missing 'spec.install.spec' field" )
325- }
326-
327- deployments , ok := installSpec ["deployments" ].([]interface {})
328- if ! ok || len (deployments ) == 0 {
329- return errors .New ("CSV missing 'spec.install.spec.deployments' field or deployments array is empty" )
330- }
331-
332- deployment , ok := deployments [0 ].(map [string ]interface {})
333- if ! ok {
334- return errors .New ("invalid deployment structure in CSV" )
335- }
336-
337- deploymentSpec , ok := deployment ["spec" ].(map [string ]interface {})
338- if ! ok {
339- return errors .New ("CSV missing deployment spec" )
340+ // Validate structure
341+ if len (csvData .Spec .Install .Spec .Deployments ) == 0 {
342+ return errors .New ("CSV missing deployments" )
340343 }
341-
342- template , ok := deploymentSpec ["template" ].(map [string ]interface {})
343- if ! ok {
344- return errors .New ("CSV missing deployment template" )
345- }
346-
347- podSpec , ok := template ["spec" ].(map [string ]interface {})
348- if ! ok {
349- return errors .New ("CSV missing pod spec" )
350- }
351-
352- containers , ok := podSpec ["containers" ].([]interface {})
353- if ! ok || len (containers ) == 0 {
354- return errors .New ("CSV missing containers or containers array is empty" )
344+ if len (csvData .Spec .Install .Spec .Deployments [0 ].Spec .Template .Spec .Containers ) == 0 {
345+ return errors .New ("CSV missing containers" )
355346 }
356347
357- container , ok := containers [0 ].(map [string ]interface {})
358- if ! ok {
359- return errors .New ("invalid container structure in CSV" )
360- }
348+ // Get reference to the first container
349+ container := & csvData .Spec .Install .Spec .Deployments [0 ].Spec .Template .Spec .Containers [0 ]
361350
362351 // Patch operator image if it exists in localImages
363352 // Use the actual detected image reference to handle fallback branding cases
364- operatorImageKey := "stackrox-operator:" + mainImageTag
353+ operatorImageKey := "stackrox-operator:" + operatorTag
365354 if imageRef , ok := localImages [operatorImageKey ]; ok {
366- container ["image" ] = imageRef
367- }
368-
369- // Patch RELATED_IMAGE_* environment variables
370- envVars , ok := container ["env" ].([]interface {})
371- if ! ok {
372- return errors .New ("CSV missing container env variables" )
355+ container .Image = imageRef
373356 }
374357
375358 // Build a reverse map from image name to full image reference for quick lookup
@@ -383,25 +366,18 @@ func patchCSVWithLocalImages(csvFile, mainImageTag string, localImages map[strin
383366 }
384367 }
385368
386- for _ , envVar := range envVars {
387- envMap , ok := envVar .(map [string ]interface {})
388- if ! ok {
389- continue
390- }
391-
392- envName , ok := envMap ["name" ].(string )
393- if ! ok {
394- continue
395- }
369+ // Patch RELATED_IMAGE_* environment variables
370+ for i := range container .Env {
371+ envVar := & container .Env [i ]
396372
397373 // Check if this is a RELATED_IMAGE_* env var
398- if ! strings .HasPrefix (envName , "RELATED_IMAGE_" ) {
374+ if ! strings .HasPrefix (envVar . Name , "RELATED_IMAGE_" ) {
399375 continue
400376 }
401377
402378 // Convert RELATED_IMAGE_FOO_BAR to foo-bar
403379 // e.g., RELATED_IMAGE_SCANNER_V4_DB → scanner-v4-db
404- imageName := envVarToImageName (envName )
380+ imageName := envVarToImageName (envVar . Name )
405381
406382 // Special case: scanner-v4-indexer and scanner-v4-matcher both use the scanner-v4 image
407383 // The same image runs in different modes based on runtime configuration
@@ -412,7 +388,7 @@ func patchCSVWithLocalImages(csvFile, mainImageTag string, localImages map[strin
412388 // Check if we have this image locally and use the actual detected reference
413389 // This handles cases where an image was found at a fallback branding org
414390 if imageRef , found := imageNameToRef [imageName ]; found {
415- envMap [ "value" ] = imageRef
391+ envVar . Value = imageRef
416392 }
417393 }
418394
0 commit comments