From a4ca065f6ee5c87686ef1482eb80ba7b6fa4e599 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Wed, 10 Dec 2025 20:47:09 +0000 Subject: [PATCH 01/32] feat: RHIDP-9762-consume-catalog-index Signed-off-by: Fortune Ndlovu --- ...kstage-operator.clusterserviceversion.yaml | 2 +- ...kstage-operator.clusterserviceversion.yaml | 6 +- .../rhdh-default-config_v1_configmap.yaml | 2 + .../rhdh/default-config/deployment.yaml | 2 + .../rhdh/patches/deployment-patch.yaml | 2 + dist/rhdh/install.yaml | 4 ++ docs/dynamic-plugins.md | 37 ++++++++++++ pkg/model/deployment.go | 18 ++++-- pkg/model/deployment_test.go | 58 +++++++++++++++++++ 9 files changed, 124 insertions(+), 7 deletions(-) diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index 3ffa82502..85b4a4159 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -35,7 +35,7 @@ metadata: } } ] - createdAt: "2025-12-02T11:27:28Z" + createdAt: "2025-12-10T20:45:39Z" description: Backstage Operator operators.operatorframework.io/builder: operator-sdk-v1.37.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index ec61610c9..5432ebb27 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.9 - createdAt: "2025-12-08T08:32:34Z" + createdAt: "2025-12-10T20:45:42Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed @@ -338,6 +338,8 @@ spec: value: quay.io/fedora/postgresql-15:latest - name: RELATED_IMAGE_backstage value: quay.io/rhdh-community/rhdh:next + - name: RELATED_IMAGE_catalog_index + value: quay.io/rhdh/plugin-catalog-index:1.9 image: quay.io/rhdh/rhdh-rhel9-operator:1.9 livenessProbe: httpGet: @@ -459,5 +461,7 @@ spec: name: postgresql - image: quay.io/rhdh-community/rhdh:next name: backstage + - image: quay.io/rhdh/plugin-catalog-index:1.9 + name: catalog-index replaces: rhdh-operator.v1.8.0 version: 1.9.0 diff --git a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml index f184b8b88..999a30bc6 100644 --- a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml @@ -236,6 +236,8 @@ data: value: /opt/app-root/src/.npmrc.dynamic-plugins/.npmrc - name: MAX_ENTRY_SIZE value: "30000000" + - name: CATALOG_INDEX_IMAGE + value: "quay.io/rhdh/plugin-catalog-index:1.9" volumeMounts: - mountPath: /dynamic-plugins-root name: dynamic-plugins-root diff --git a/config/profile/rhdh/default-config/deployment.yaml b/config/profile/rhdh/default-config/deployment.yaml index 74ba450fd..95713326d 100644 --- a/config/profile/rhdh/default-config/deployment.yaml +++ b/config/profile/rhdh/default-config/deployment.yaml @@ -60,6 +60,8 @@ spec: value: /opt/app-root/src/.npmrc.dynamic-plugins/.npmrc - name: MAX_ENTRY_SIZE value: "30000000" + - name: CATALOG_INDEX_IMAGE + value: "quay.io/rhdh/plugin-catalog-index:1.9" volumeMounts: - mountPath: /dynamic-plugins-root name: dynamic-plugins-root diff --git a/config/profile/rhdh/patches/deployment-patch.yaml b/config/profile/rhdh/patches/deployment-patch.yaml index 62daa5a21..788c85337 100644 --- a/config/profile/rhdh/patches/deployment-patch.yaml +++ b/config/profile/rhdh/patches/deployment-patch.yaml @@ -28,4 +28,6 @@ spec: value: quay.io/fedora/postgresql-15:latest - name: RELATED_IMAGE_backstage value: quay.io/rhdh-community/rhdh:next + - name: RELATED_IMAGE_catalog_index + value: quay.io/rhdh/plugin-catalog-index:1.9 diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index 5303c4666..c7588d0f1 100644 --- a/dist/rhdh/install.yaml +++ b/dist/rhdh/install.yaml @@ -2088,6 +2088,8 @@ data: value: /opt/app-root/src/.npmrc.dynamic-plugins/.npmrc - name: MAX_ENTRY_SIZE value: "30000000" + - name: CATALOG_INDEX_IMAGE + value: "quay.io/rhdh/plugin-catalog-index:1.9" volumeMounts: - mountPath: /dynamic-plugins-root name: dynamic-plugins-root @@ -2948,6 +2950,8 @@ spec: value: quay.io/fedora/postgresql-15:latest - name: RELATED_IMAGE_backstage value: quay.io/rhdh-community/rhdh:next + - name: RELATED_IMAGE_catalog_index + value: quay.io/rhdh/plugin-catalog-index:1.9 image: quay.io/rhdh/rhdh-rhel9-operator:1.9 livenessProbe: httpGet: diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index 1d7cb724c..9d00418de 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -43,6 +43,43 @@ spec: TODO: Dynamic plugins can be configured to use container registries for authentication and image pulling. This section should cover the configuration options available for container registry integration with dynamic plugins. +## Catalog Index Configuration + +The catalog index is an OCI artifact that contains `dynamic-plugins.default.yaml`, which defines the default set of dynamic plugins to be installed. The operator automatically configures the `install-dynamic-plugins` init container to pull and extract this catalog index. + +By default, the operator sets `CATALOG_INDEX_IMAGE` environment variable in the `install-dynamic-plugins` init container: + +```yaml +env: + - name: CATALOG_INDEX_IMAGE + value: "quay.io/rhdh/plugin-catalog-index:1.9" +``` + +The `install-dynamic-plugins.py` script: +1. Pulls the catalog index OCI image using `skopeo` +2. Extracts the image layers to a temporary directory (`.catalog-index-tmp`) +3. locates `dynamic-plugins.default.yaml` within the extracted content +4. Replaces the `dynamic-plugins.default.yaml` reference in your `includes` list with the extracted catalog index version + +### Overriding the Catalog Index Image + +To use a different catalog index, such as a newer version or a mirrored image, use the `extraEnvs` field in your Backstage CR: + +```yaml +apiVersion: rhdh.redhat.com/v1alpha5 +kind: Backstage +metadata: + name: my-backstage +spec: + application: + extraEnvs: + envs: + - name: CATALOG_INDEX_IMAGE + value: "quay.io/rhdh/plugin-catalog-index: 1.9" + containers: + - install-dynamic-plugins +``` + ## Dynamic plugins dependency management ### Overview diff --git a/pkg/model/deployment.go b/pkg/model/deployment.go index 3fd3935da..e9cdf86d2 100644 --- a/pkg/model/deployment.go +++ b/pkg/model/deployment.go @@ -285,6 +285,7 @@ func (b *BackstageDeployment) setImage(image *string) { } // adds environment from source to the Backstage Container +// If an env var with the same name already exists, it will be replaced (not duplicated) func (b *BackstageDeployment) addExtraEnvs(extraEnvs *bsv1.ExtraEnvs) error { if extraEnvs == nil { return nil @@ -296,16 +297,23 @@ func (b *BackstageDeployment) addExtraEnvs(extraEnvs *bsv1.ExtraEnvs) error { return fmt.Errorf("can not get containers to add env %s: %w", env.Name, err) } for _, container := range containers { - container.Env = - append(container.Env, corev1.EnvVar{ - Name: env.Name, - Value: env.Value, - }) + b.setOrAppendEnvVar(container, env.Name, env.Value) } } return nil } +// setOrAppendEnvVar sets an env var on a container, replacing if it exists or appending if not +func (b *BackstageDeployment) setOrAppendEnvVar(container *corev1.Container, name, value string) { + for i, existingEnv := range container.Env { + if existingEnv.Name == name { + container.Env[i] = corev1.EnvVar{Name: name, Value: value} + return + } + } + container.Env = append(container.Env, corev1.EnvVar{Name: name, Value: value}) +} + // MountFilesFrom adds Volume to specified podSpec and related VolumeMounts to specified belonging to this podSpec container // from ConfigMap or Secret volume source // containers - array of containers to add VolumeMount(s) to diff --git a/pkg/model/deployment_test.go b/pkg/model/deployment_test.go index 6b2ea8266..28bb8c064 100644 --- a/pkg/model/deployment_test.go +++ b/pkg/model/deployment_test.go @@ -7,6 +7,7 @@ import ( "github.com/redhat-developer/rhdh-operator/pkg/platform" appv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/utils/ptr" @@ -270,6 +271,63 @@ spec: } +// TestExtraEnvsReplacesExistingEnvVar verifies that when a user specifies an env var +// via extraEnvs that already exists in the default config, it replaces the existing +// value instead of creating duplicates (which would cause Kubernetes to reject the deployment) +func TestExtraEnvsReplacesExistingEnvVar(t *testing.T) { + bs := *deploymentTestBackstage.DeepCopy() + + // Add an env var via patch that will already exist in the deployment + bs.Spec.Deployment = &bsv1.BackstageDeployment{} + bs.Spec.Deployment.Patch = &apiextensionsv1.JSON{ + Raw: []byte(` +spec: + template: + spec: + initContainers: + - name: install-dynamic-plugins + env: + - name: CATALOG_INDEX_IMAGE + value: "quay.io/rhdh/plugin-catalog-index:1.9" +`), + } + + // Now specify the same env var via extraEnvs with a different value + bs.Spec.Application.ExtraEnvs = &bsv1.ExtraEnvs{ + Envs: []bsv1.Env{ + {Name: "CATALOG_INDEX_IMAGE", Value: "quay.io/rhdh/plugin-catalog-index:1.8", Containers: []string{"install-dynamic-plugins"}}, + }, + } + + testObj := createBackstageTest(bs).withDefaultConfig(true) + + model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) + assert.NoError(t, err) + assert.NotNil(t, model.backstageDeployment) + + // Find the init container + var initContainerEnvs []corev1.EnvVar + for _, ic := range model.backstageDeployment.podSpec().InitContainers { + if ic.Name == "install-dynamic-plugins" { + initContainerEnvs = ic.Env + break + } + } + + // Verify there's no duplicate - only ONE CATALOG_INDEX_IMAGE env var should exist + catalogIndexCount := 0 + var catalogIndexValue string + for _, env := range initContainerEnvs { + if env.Name == "CATALOG_INDEX_IMAGE" { + catalogIndexCount++ + catalogIndexValue = env.Value + } + } + + assert.Equal(t, 1, catalogIndexCount, "CATALOG_INDEX_IMAGE should appear exactly once, not duplicated") + assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.8", catalogIndexValue, "extraEnvs value should override the default") +} + func TestDeploymentKind(t *testing.T) { bs := *deploymentTestBackstage.DeepCopy() From d12045fe7c5ed394dcecc516c5e6ebc18d5de048 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Wed, 10 Dec 2025 20:56:02 +0000 Subject: [PATCH 02/32] typo Signed-off-by: Fortune Ndlovu --- docs/dynamic-plugins.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index 9d00418de..b8219bf84 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -75,7 +75,7 @@ spec: extraEnvs: envs: - name: CATALOG_INDEX_IMAGE - value: "quay.io/rhdh/plugin-catalog-index: 1.9" + value: "quay.io/rhdh/plugin-catalog-index:1.9" containers: - install-dynamic-plugins ``` From 174742712f92bf8fc3a8e6ec1f8e0dfdc112b9af Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Thu, 11 Dec 2025 12:26:47 +0000 Subject: [PATCH 03/32] Add RHIDP-9762-consume-catalog-index Signed-off-by: Fortune Ndlovu --- docs/dynamic-plugins.md | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index b8219bf84..311277d41 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -45,25 +45,13 @@ TODO: Dynamic plugins can be configured to use container registries for authenti ## Catalog Index Configuration -The catalog index is an OCI artifact that contains `dynamic-plugins.default.yaml`, which defines the default set of dynamic plugins to be installed. The operator automatically configures the `install-dynamic-plugins` init container to pull and extract this catalog index. +The operator supports loading default plugin configurations from an OCI container image (catalog index). For general information about how the catalog index works, see [Using a Catalog Index Image for Default Plugin Configurations](https://github.com/redhat-developer/rhdh/blob/main/docs/dynamic-plugins/installing-plugins.md#using-a-catalog-index-image-for-default-plugin-configurations). -By default, the operator sets `CATALOG_INDEX_IMAGE` environment variable in the `install-dynamic-plugins` init container: - -```yaml -env: - - name: CATALOG_INDEX_IMAGE - value: "quay.io/rhdh/plugin-catalog-index:1.9" -``` - -The `install-dynamic-plugins.py` script: -1. Pulls the catalog index OCI image using `skopeo` -2. Extracts the image layers to a temporary directory (`.catalog-index-tmp`) -3. locates `dynamic-plugins.default.yaml` within the extracted content -4. Replaces the `dynamic-plugins.default.yaml` reference in your `includes` list with the extracted catalog index version +By default, the operator sets `CATALOG_INDEX_IMAGE` to `quay.io/rhdh/plugin-catalog-index:1.9` in the `install-dynamic-plugins` init container. ### Overriding the Catalog Index Image -To use a different catalog index, such as a newer version or a mirrored image, use the `extraEnvs` field in your Backstage CR: +To use a different catalog index image, such as a newer version or a mirrored image, use the `extraEnvs` field in your Backstage CR: ```yaml apiVersion: rhdh.redhat.com/v1alpha5 From b9362aeb71f592d7c344b8e3bccea9521609f6d4 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Thu, 11 Dec 2025 12:30:30 +0000 Subject: [PATCH 04/32] Update docs Signed-off-by: Fortune Ndlovu --- docs/dynamic-plugins.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index 311277d41..0b6b427e7 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -48,9 +48,6 @@ TODO: Dynamic plugins can be configured to use container registries for authenti The operator supports loading default plugin configurations from an OCI container image (catalog index). For general information about how the catalog index works, see [Using a Catalog Index Image for Default Plugin Configurations](https://github.com/redhat-developer/rhdh/blob/main/docs/dynamic-plugins/installing-plugins.md#using-a-catalog-index-image-for-default-plugin-configurations). By default, the operator sets `CATALOG_INDEX_IMAGE` to `quay.io/rhdh/plugin-catalog-index:1.9` in the `install-dynamic-plugins` init container. - -### Overriding the Catalog Index Image - To use a different catalog index image, such as a newer version or a mirrored image, use the `extraEnvs` field in your Backstage CR: ```yaml From 575cbf8511cfd6fb3b76aff35100ff0f29334c0b Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Fri, 12 Dec 2025 14:34:17 +0000 Subject: [PATCH 05/32] Update test values and docs Signed-off-by: Fortune Ndlovu --- docs/dynamic-plugins.md | 6 ++++++ pkg/model/deployment_test.go | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index 0b6b427e7..8c04bea21 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -65,6 +65,12 @@ spec: - install-dynamic-plugins ``` +### Using a Private Registry + +If your catalog index image is stored in a private registry that requires authentication, create a secret named `-dynamic-plugins-registry-auth` containing an `auth.json` file with your registry credentials. + +For detailed instructions on configuring private registry authentication, see the [official Red Hat Developer Hub documentation](https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.8/html/installing_and_viewing_plugins_in_red_hat_developer_hub/assembly-third-party-plugins#proc-load-plugin-oci-image_assembly-install-third-party-plugins-rhdh). + ## Dynamic plugins dependency management ### Overview diff --git a/pkg/model/deployment_test.go b/pkg/model/deployment_test.go index 28bb8c064..651db925f 100644 --- a/pkg/model/deployment_test.go +++ b/pkg/model/deployment_test.go @@ -288,14 +288,14 @@ spec: - name: install-dynamic-plugins env: - name: CATALOG_INDEX_IMAGE - value: "quay.io/rhdh/plugin-catalog-index:1.9" + value: "quay.io/rhdh/plugin-catalog-index:1.8" `), } - // Now specify the same env var via extraEnvs with a different value + // Now specify the same env var via extraEnvs with a different value (upgrading to latest) bs.Spec.Application.ExtraEnvs = &bsv1.ExtraEnvs{ Envs: []bsv1.Env{ - {Name: "CATALOG_INDEX_IMAGE", Value: "quay.io/rhdh/plugin-catalog-index:1.8", Containers: []string{"install-dynamic-plugins"}}, + {Name: "CATALOG_INDEX_IMAGE", Value: "quay.io/rhdh/plugin-catalog-index:1.9", Containers: []string{"install-dynamic-plugins"}}, }, } @@ -325,7 +325,7 @@ spec: } assert.Equal(t, 1, catalogIndexCount, "CATALOG_INDEX_IMAGE should appear exactly once, not duplicated") - assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.8", catalogIndexValue, "extraEnvs value should override the default") + assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.9", catalogIndexValue, "extraEnvs value should override the default") } func TestDeploymentKind(t *testing.T) { From e6f86598927bfea569d2f0670ba9038a3349eb44 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Fri, 12 Dec 2025 14:53:55 +0000 Subject: [PATCH 06/32] the extraEnvs now just appends env vars as before. Since Kubernetes uses the last value when duplicates exist, and extraEnvs is processed after the patch, user overrides will still take effect Signed-off-by: Fortune Ndlovu --- pkg/model/deployment.go | 17 +++-------- pkg/model/deployment_test.go | 58 ------------------------------------ 2 files changed, 4 insertions(+), 71 deletions(-) diff --git a/pkg/model/deployment.go b/pkg/model/deployment.go index e9cdf86d2..c6010f866 100644 --- a/pkg/model/deployment.go +++ b/pkg/model/deployment.go @@ -285,7 +285,6 @@ func (b *BackstageDeployment) setImage(image *string) { } // adds environment from source to the Backstage Container -// If an env var with the same name already exists, it will be replaced (not duplicated) func (b *BackstageDeployment) addExtraEnvs(extraEnvs *bsv1.ExtraEnvs) error { if extraEnvs == nil { return nil @@ -297,23 +296,15 @@ func (b *BackstageDeployment) addExtraEnvs(extraEnvs *bsv1.ExtraEnvs) error { return fmt.Errorf("can not get containers to add env %s: %w", env.Name, err) } for _, container := range containers { - b.setOrAppendEnvVar(container, env.Name, env.Value) + container.Env = append(container.Env, corev1.EnvVar{ + Name: env.Name, + Value: env.Value, + }) } } return nil } -// setOrAppendEnvVar sets an env var on a container, replacing if it exists or appending if not -func (b *BackstageDeployment) setOrAppendEnvVar(container *corev1.Container, name, value string) { - for i, existingEnv := range container.Env { - if existingEnv.Name == name { - container.Env[i] = corev1.EnvVar{Name: name, Value: value} - return - } - } - container.Env = append(container.Env, corev1.EnvVar{Name: name, Value: value}) -} - // MountFilesFrom adds Volume to specified podSpec and related VolumeMounts to specified belonging to this podSpec container // from ConfigMap or Secret volume source // containers - array of containers to add VolumeMount(s) to diff --git a/pkg/model/deployment_test.go b/pkg/model/deployment_test.go index 651db925f..6b2ea8266 100644 --- a/pkg/model/deployment_test.go +++ b/pkg/model/deployment_test.go @@ -7,7 +7,6 @@ import ( "github.com/redhat-developer/rhdh-operator/pkg/platform" appv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/utils/ptr" @@ -271,63 +270,6 @@ spec: } -// TestExtraEnvsReplacesExistingEnvVar verifies that when a user specifies an env var -// via extraEnvs that already exists in the default config, it replaces the existing -// value instead of creating duplicates (which would cause Kubernetes to reject the deployment) -func TestExtraEnvsReplacesExistingEnvVar(t *testing.T) { - bs := *deploymentTestBackstage.DeepCopy() - - // Add an env var via patch that will already exist in the deployment - bs.Spec.Deployment = &bsv1.BackstageDeployment{} - bs.Spec.Deployment.Patch = &apiextensionsv1.JSON{ - Raw: []byte(` -spec: - template: - spec: - initContainers: - - name: install-dynamic-plugins - env: - - name: CATALOG_INDEX_IMAGE - value: "quay.io/rhdh/plugin-catalog-index:1.8" -`), - } - - // Now specify the same env var via extraEnvs with a different value (upgrading to latest) - bs.Spec.Application.ExtraEnvs = &bsv1.ExtraEnvs{ - Envs: []bsv1.Env{ - {Name: "CATALOG_INDEX_IMAGE", Value: "quay.io/rhdh/plugin-catalog-index:1.9", Containers: []string{"install-dynamic-plugins"}}, - }, - } - - testObj := createBackstageTest(bs).withDefaultConfig(true) - - model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) - assert.NoError(t, err) - assert.NotNil(t, model.backstageDeployment) - - // Find the init container - var initContainerEnvs []corev1.EnvVar - for _, ic := range model.backstageDeployment.podSpec().InitContainers { - if ic.Name == "install-dynamic-plugins" { - initContainerEnvs = ic.Env - break - } - } - - // Verify there's no duplicate - only ONE CATALOG_INDEX_IMAGE env var should exist - catalogIndexCount := 0 - var catalogIndexValue string - for _, env := range initContainerEnvs { - if env.Name == "CATALOG_INDEX_IMAGE" { - catalogIndexCount++ - catalogIndexValue = env.Value - } - } - - assert.Equal(t, 1, catalogIndexCount, "CATALOG_INDEX_IMAGE should appear exactly once, not duplicated") - assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.9", catalogIndexValue, "extraEnvs value should override the default") -} - func TestDeploymentKind(t *testing.T) { bs := *deploymentTestBackstage.DeepCopy() From db8cf616131d6ecd2d0b830ace9b1420448731d4 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Fri, 12 Dec 2025 14:56:22 +0000 Subject: [PATCH 07/32] fix syntax --- pkg/model/deployment.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/model/deployment.go b/pkg/model/deployment.go index c6010f866..3fd3935da 100644 --- a/pkg/model/deployment.go +++ b/pkg/model/deployment.go @@ -296,10 +296,11 @@ func (b *BackstageDeployment) addExtraEnvs(extraEnvs *bsv1.ExtraEnvs) error { return fmt.Errorf("can not get containers to add env %s: %w", env.Name, err) } for _, container := range containers { - container.Env = append(container.Env, corev1.EnvVar{ - Name: env.Name, - Value: env.Value, - }) + container.Env = + append(container.Env, corev1.EnvVar{ + Name: env.Name, + Value: env.Value, + }) } } return nil From 985656794c44db9f9d363a29b19b529117d020e3 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Tue, 16 Dec 2025 11:41:27 +0000 Subject: [PATCH 08/32] Check if existingEnv exists if so replace it with a new value, if it does not exist append new env var Signed-off-by: Fortune Ndlovu --- pkg/model/deployment.go | 18 +++++++---- pkg/model/deployment_test.go | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/pkg/model/deployment.go b/pkg/model/deployment.go index 3fd3935da..e9cdf86d2 100644 --- a/pkg/model/deployment.go +++ b/pkg/model/deployment.go @@ -285,6 +285,7 @@ func (b *BackstageDeployment) setImage(image *string) { } // adds environment from source to the Backstage Container +// If an env var with the same name already exists, it will be replaced (not duplicated) func (b *BackstageDeployment) addExtraEnvs(extraEnvs *bsv1.ExtraEnvs) error { if extraEnvs == nil { return nil @@ -296,16 +297,23 @@ func (b *BackstageDeployment) addExtraEnvs(extraEnvs *bsv1.ExtraEnvs) error { return fmt.Errorf("can not get containers to add env %s: %w", env.Name, err) } for _, container := range containers { - container.Env = - append(container.Env, corev1.EnvVar{ - Name: env.Name, - Value: env.Value, - }) + b.setOrAppendEnvVar(container, env.Name, env.Value) } } return nil } +// setOrAppendEnvVar sets an env var on a container, replacing if it exists or appending if not +func (b *BackstageDeployment) setOrAppendEnvVar(container *corev1.Container, name, value string) { + for i, existingEnv := range container.Env { + if existingEnv.Name == name { + container.Env[i] = corev1.EnvVar{Name: name, Value: value} + return + } + } + container.Env = append(container.Env, corev1.EnvVar{Name: name, Value: value}) +} + // MountFilesFrom adds Volume to specified podSpec and related VolumeMounts to specified belonging to this podSpec container // from ConfigMap or Secret volume source // containers - array of containers to add VolumeMount(s) to diff --git a/pkg/model/deployment_test.go b/pkg/model/deployment_test.go index 6b2ea8266..651db925f 100644 --- a/pkg/model/deployment_test.go +++ b/pkg/model/deployment_test.go @@ -7,6 +7,7 @@ import ( "github.com/redhat-developer/rhdh-operator/pkg/platform" appv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/utils/ptr" @@ -270,6 +271,63 @@ spec: } +// TestExtraEnvsReplacesExistingEnvVar verifies that when a user specifies an env var +// via extraEnvs that already exists in the default config, it replaces the existing +// value instead of creating duplicates (which would cause Kubernetes to reject the deployment) +func TestExtraEnvsReplacesExistingEnvVar(t *testing.T) { + bs := *deploymentTestBackstage.DeepCopy() + + // Add an env var via patch that will already exist in the deployment + bs.Spec.Deployment = &bsv1.BackstageDeployment{} + bs.Spec.Deployment.Patch = &apiextensionsv1.JSON{ + Raw: []byte(` +spec: + template: + spec: + initContainers: + - name: install-dynamic-plugins + env: + - name: CATALOG_INDEX_IMAGE + value: "quay.io/rhdh/plugin-catalog-index:1.8" +`), + } + + // Now specify the same env var via extraEnvs with a different value (upgrading to latest) + bs.Spec.Application.ExtraEnvs = &bsv1.ExtraEnvs{ + Envs: []bsv1.Env{ + {Name: "CATALOG_INDEX_IMAGE", Value: "quay.io/rhdh/plugin-catalog-index:1.9", Containers: []string{"install-dynamic-plugins"}}, + }, + } + + testObj := createBackstageTest(bs).withDefaultConfig(true) + + model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) + assert.NoError(t, err) + assert.NotNil(t, model.backstageDeployment) + + // Find the init container + var initContainerEnvs []corev1.EnvVar + for _, ic := range model.backstageDeployment.podSpec().InitContainers { + if ic.Name == "install-dynamic-plugins" { + initContainerEnvs = ic.Env + break + } + } + + // Verify there's no duplicate - only ONE CATALOG_INDEX_IMAGE env var should exist + catalogIndexCount := 0 + var catalogIndexValue string + for _, env := range initContainerEnvs { + if env.Name == "CATALOG_INDEX_IMAGE" { + catalogIndexCount++ + catalogIndexValue = env.Value + } + } + + assert.Equal(t, 1, catalogIndexCount, "CATALOG_INDEX_IMAGE should appear exactly once, not duplicated") + assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.9", catalogIndexValue, "extraEnvs value should override the default") +} + func TestDeploymentKind(t *testing.T) { bs := *deploymentTestBackstage.DeepCopy() From 4fdd3770e5b65d7937e526fde83032cc5eb894f8 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Tue, 16 Dec 2025 12:28:17 +0000 Subject: [PATCH 09/32] Look at operator's own env var RELATED_IMAGE_catalog_index, If it exists, copy that value to CATALOG_INDEX_IMAGE on the init container Signed-off-by: Fortune Ndlovu --- pkg/model/deployment.go | 1 + pkg/model/dynamic-plugins.go | 13 +++++++++++-- pkg/model/dynamic-plugins_test.go | 32 +++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/pkg/model/deployment.go b/pkg/model/deployment.go index e9cdf86d2..608aee7c0 100644 --- a/pkg/model/deployment.go +++ b/pkg/model/deployment.go @@ -29,6 +29,7 @@ const ( ) const BackstageImageEnvVar = "RELATED_IMAGE_backstage" +const CatalogIndexImageEnvVar = "RELATED_IMAGE_catalog_index" const DefaultMountDir = "/opt/app-root/src" const ExtConfigHashAnnotation = "rhdh.redhat.com/ext-config-hash" diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index ffcb6c9fa..eb5fd1a40 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -273,12 +273,21 @@ func (p *DynamicPlugins) mergeWith(specData string) (string, error) { } func (p *DynamicPlugins) getInitContainer() (int, *corev1.Container) { i, initContainer := DynamicPluginsInitContainer(p.model.backstageDeployment.podSpec().InitContainers) + if i < 0 || initContainer == nil { + return i, initContainer + } + actualInitContainer := &p.model.backstageDeployment.podSpec().InitContainers[i] // override image with env var if os.Getenv(BackstageImageEnvVar) != "" { - initContainer.Image = os.Getenv(BackstageImageEnvVar) + actualInitContainer.Image = os.Getenv(BackstageImageEnvVar) + } + + if catalogIndexImage := os.Getenv(CatalogIndexImageEnvVar); catalogIndexImage != "" { + p.model.backstageDeployment.setOrAppendEnvVar(actualInitContainer, "CATALOG_INDEX_IMAGE", catalogIndexImage) } - return i, initContainer + + return i, actualInitContainer } // returns initContainer supposed to initialize DynamicPlugins diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index 2364f42f1..51ccc7ec7 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -247,6 +247,38 @@ func initContainer(model *BackstageModel) *corev1.Container { return nil } +// TestCatalogIndexImageEnvVar verifies that the operator reads RELATED_IMAGE_catalog_index +// and sets CATALOG_INDEX_IMAGE on the install-dynamic-plugins init container +func TestCatalogIndexImageEnvVar(t *testing.T) { + bs := testDynamicPluginsBackstage.DeepCopy() + + testObj := createBackstageTest(*bs).withDefaultConfig(true). + addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). + addToDefaultConfig("deployment.yaml", "sidecar-deployment.yaml") + + // Set the RELATED_IMAGE_catalog_index env var (simulating operator environment) + t.Setenv(CatalogIndexImageEnvVar, "quay.io/rhdh/plugin-catalog-index:1.9") + + model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) + assert.NoError(t, err) + assert.NotNil(t, model.backstageDeployment) + + ic := initContainer(model) + assert.NotNil(t, ic) + + // Verify CATALOG_INDEX_IMAGE is set on the init container + var catalogIndexValue string + for _, env := range ic.Env { + if env.Name == "CATALOG_INDEX_IMAGE" { + catalogIndexValue = env.Value + break + } + } + + assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.9", catalogIndexValue, + "CATALOG_INDEX_IMAGE should be set from RELATED_IMAGE_catalog_index") +} + func TestUnmarshalDynaPluginsConfig(t *testing.T) { yamlData := ` plugins: From 17baa0c8ccfaf7be82b52b29076f84f822451108 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Tue, 16 Dec 2025 12:43:18 +0000 Subject: [PATCH 10/32] Update docs Signed-off-by: Fortune Ndlovu --- docs/dynamic-plugins.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index 8c04bea21..2d2b3576e 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -47,7 +47,7 @@ TODO: Dynamic plugins can be configured to use container registries for authenti The operator supports loading default plugin configurations from an OCI container image (catalog index). For general information about how the catalog index works, see [Using a Catalog Index Image for Default Plugin Configurations](https://github.com/redhat-developer/rhdh/blob/main/docs/dynamic-plugins/installing-plugins.md#using-a-catalog-index-image-for-default-plugin-configurations). -By default, the operator sets `CATALOG_INDEX_IMAGE` to `quay.io/rhdh/plugin-catalog-index:1.9` in the `install-dynamic-plugins` init container. +By default, rhdh profile of operator sets `CATALOG_INDEX_IMAGE` to `quay.io/rhdh/plugin-catalog-index:1.9` in the `install-dynamic-plugins` init container. To use a different catalog index image, such as a newer version or a mirrored image, use the `extraEnvs` field in your Backstage CR: ```yaml @@ -65,12 +65,6 @@ spec: - install-dynamic-plugins ``` -### Using a Private Registry - -If your catalog index image is stored in a private registry that requires authentication, create a secret named `-dynamic-plugins-registry-auth` containing an `auth.json` file with your registry credentials. - -For detailed instructions on configuring private registry authentication, see the [official Red Hat Developer Hub documentation](https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.8/html/installing_and_viewing_plugins_in_red_hat_developer_hub/assembly-third-party-plugins#proc-load-plugin-oci-image_assembly-install-third-party-plugins-rhdh). - ## Dynamic plugins dependency management ### Overview From 13d7a64164ef06c95d0b195ca2816f3f8e482f8f Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Wed, 17 Dec 2025 12:39:22 +0000 Subject: [PATCH 11/32] Update TestCatalogIndexImageEnvVar to use janus-deployment.yaml instead of sidecar-deployment.yaml Signed-off-by: Fortune Ndlovu --- pkg/model/dynamic-plugins_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index 51ccc7ec7..e49245a9b 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -254,7 +254,7 @@ func TestCatalogIndexImageEnvVar(t *testing.T) { testObj := createBackstageTest(*bs).withDefaultConfig(true). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). - addToDefaultConfig("deployment.yaml", "sidecar-deployment.yaml") + addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") // Set the RELATED_IMAGE_catalog_index env var (simulating operator environment) t.Setenv(CatalogIndexImageEnvVar, "quay.io/rhdh/plugin-catalog-index:1.9") From bf6f2dd3d9a49d28ed72b44ce12caaf6b1f4e4e0 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Wed, 17 Dec 2025 13:13:00 +0000 Subject: [PATCH 12/32] Fix: Added a check to only set CATALOG_INDEX_IMAGE from the operator env var if the user hasn't already specified it Signed-off-by: Fortune Ndlovu --- pkg/model/dynamic-plugins.go | 16 ++++++++++++- pkg/model/dynamic-plugins_test.go | 40 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index eb5fd1a40..1b12b4708 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -283,13 +283,27 @@ func (p *DynamicPlugins) getInitContainer() (int, *corev1.Container) { actualInitContainer.Image = os.Getenv(BackstageImageEnvVar) } + // Only set CATALOG_INDEX_IMAGE from operator env var if user hasn't already specified it + // (e.g., via extraEnvs or deployment patch) if catalogIndexImage := os.Getenv(CatalogIndexImageEnvVar); catalogIndexImage != "" { - p.model.backstageDeployment.setOrAppendEnvVar(actualInitContainer, "CATALOG_INDEX_IMAGE", catalogIndexImage) + if !hasEnvVar(actualInitContainer, "CATALOG_INDEX_IMAGE") { + p.model.backstageDeployment.setOrAppendEnvVar(actualInitContainer, "CATALOG_INDEX_IMAGE", catalogIndexImage) + } } return i, actualInitContainer } +// hasEnvVar checks if a container already has an environment variable with the given name +func hasEnvVar(container *corev1.Container, name string) bool { + for _, env := range container.Env { + if env.Name == name { + return true + } + } + return false +} + // returns initContainer supposed to initialize DynamicPlugins // TODO consider to use a label to identify instead func DynamicPluginsInitContainer(initContainers []corev1.Container) (int, *corev1.Container) { diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index e49245a9b..38cc25a72 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -279,6 +279,46 @@ func TestCatalogIndexImageEnvVar(t *testing.T) { "CATALOG_INDEX_IMAGE should be set from RELATED_IMAGE_catalog_index") } +// TestCatalogIndexImageExtraEnvsOverride verifies that user-specified extraEnvs +// takes precedence over the operator's RELATED_IMAGE_catalog_index env var +func TestCatalogIndexImageExtraEnvsOverride(t *testing.T) { + bs := testDynamicPluginsBackstage.DeepCopy() + + // User specifies a different catalog index image via extraEnvs + bs.Spec.Application.ExtraEnvs = &bsv1.ExtraEnvs{ + Envs: []bsv1.Env{ + {Name: "CATALOG_INDEX_IMAGE", Value: "quay.io/rhdh/plugin-catalog-index:1.8", Containers: []string{"install-dynamic-plugins"}}, + }, + } + + testObj := createBackstageTest(*bs).withDefaultConfig(true). + addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). + addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") + + // Set the RELATED_IMAGE_catalog_index env var (simulating operator environment) + // This should NOT override the user's extraEnvs value + t.Setenv(CatalogIndexImageEnvVar, "quay.io/rhdh/plugin-catalog-index:1.9") + + model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) + assert.NoError(t, err) + assert.NotNil(t, model.backstageDeployment) + + ic := initContainer(model) + assert.NotNil(t, ic) + + // Verify user's extraEnvs value takes precedence + var catalogIndexValue string + for _, env := range ic.Env { + if env.Name == "CATALOG_INDEX_IMAGE" { + catalogIndexValue = env.Value + break + } + } + + assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.8", catalogIndexValue, + "extraEnvs value should override the operator's RELATED_IMAGE_catalog_index") +} + func TestUnmarshalDynaPluginsConfig(t *testing.T) { yamlData := ` plugins: From 0dc45a86548635ad8016a669ffc4f9a84ebd6427 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Thu, 18 Dec 2025 12:11:34 +0000 Subject: [PATCH 13/32] update catalog index documentation and move configuration to examples folder Signed-off-by: Fortune Ndlovu --- docs/dynamic-plugins.md | 19 ++----------------- examples/catalog-index.yaml | 13 +++++++++++++ 2 files changed, 15 insertions(+), 17 deletions(-) create mode 100644 examples/catalog-index.yaml diff --git a/docs/dynamic-plugins.md b/docs/dynamic-plugins.md index 2d2b3576e..144cce3c3 100644 --- a/docs/dynamic-plugins.md +++ b/docs/dynamic-plugins.md @@ -47,23 +47,8 @@ TODO: Dynamic plugins can be configured to use container registries for authenti The operator supports loading default plugin configurations from an OCI container image (catalog index). For general information about how the catalog index works, see [Using a Catalog Index Image for Default Plugin Configurations](https://github.com/redhat-developer/rhdh/blob/main/docs/dynamic-plugins/installing-plugins.md#using-a-catalog-index-image-for-default-plugin-configurations). -By default, rhdh profile of operator sets `CATALOG_INDEX_IMAGE` to `quay.io/rhdh/plugin-catalog-index:1.9` in the `install-dynamic-plugins` init container. -To use a different catalog index image, such as a newer version or a mirrored image, use the `extraEnvs` field in your Backstage CR: - -```yaml -apiVersion: rhdh.redhat.com/v1alpha5 -kind: Backstage -metadata: - name: my-backstage -spec: - application: - extraEnvs: - envs: - - name: CATALOG_INDEX_IMAGE - value: "quay.io/rhdh/plugin-catalog-index:1.9" - containers: - - install-dynamic-plugins -``` +By default, the `rhdh` profile of operator [injects](../config/profile/rhdh/patches/deployment-patch.yaml#L31-L32) the `CATALOG_INDEX_IMAGE` environment variable in the RHDH `install-dynamic-plugins` init containers. +To use a different catalog index image, such as a newer version or a mirrored image, use the `extraEnvs` field in your Backstage CR. See [examples/catalog-index.yaml](../examples/catalog-index.yaml) for a complete example. ## Dynamic plugins dependency management diff --git a/examples/catalog-index.yaml b/examples/catalog-index.yaml new file mode 100644 index 000000000..284405b28 --- /dev/null +++ b/examples/catalog-index.yaml @@ -0,0 +1,13 @@ +apiVersion: rhdh.redhat.com/v1alpha5 +kind: Backstage +metadata: + name: my-backstage +spec: + application: + extraEnvs: + envs: + - name: CATALOG_INDEX_IMAGE + value: "quay.io/rhdh/plugin-catalog-index:1.9" + containers: + - install-dynamic-plugins + From bf354031734aacc0697bd1ec0c8c7cb112610eb3 Mon Sep 17 00:00:00 2001 From: Fortune Ndlovu Date: Thu, 18 Dec 2025 12:43:05 +0000 Subject: [PATCH 14/32] fixup: RELATED_IMAGE_catalog_index should override the default config. The presendence order should first, User CR (extraEnvs or deployment patch), second RELATED_IMAGE_catalog_index (operator env var) and thirdly, default config (fallback value in deployment.yaml) Signed-off-by: Fortune Ndlovu --- .../rhdh/default-config/deployment.yaml | 1 + pkg/model/deployment.go | 7 ++ pkg/model/dynamic-plugins.go | 17 ---- pkg/model/dynamic-plugins_test.go | 85 +++++++++++++++++++ pkg/model/testdata/janus-deployment.yaml | 3 + 5 files changed, 96 insertions(+), 17 deletions(-) diff --git a/config/profile/rhdh/default-config/deployment.yaml b/config/profile/rhdh/default-config/deployment.yaml index 95713326d..b8f6f8f13 100644 --- a/config/profile/rhdh/default-config/deployment.yaml +++ b/config/profile/rhdh/default-config/deployment.yaml @@ -60,6 +60,7 @@ spec: value: /opt/app-root/src/.npmrc.dynamic-plugins/.npmrc - name: MAX_ENTRY_SIZE value: "30000000" + # CATALOG_INDEX_IMAGE will be replaced by the value of the `RELATED_IMAGE_catalog_index` env var, if set - name: CATALOG_INDEX_IMAGE value: "quay.io/rhdh/plugin-catalog-index:1.9" volumeMounts: diff --git a/pkg/model/deployment.go b/pkg/model/deployment.go index 608aee7c0..028610970 100644 --- a/pkg/model/deployment.go +++ b/pkg/model/deployment.go @@ -112,6 +112,13 @@ func (b *BackstageDeployment) addToModel(model *BackstageModel, backstage bsv1.B b.setImage(ptr.To(os.Getenv(BackstageImageEnvVar))) } + // Set CATALOG_INDEX_IMAGE from operator env var BEFORE extraEnvs are applied, so user-specified extraEnvs can still override this value + if catalogIndexImage := os.Getenv(CatalogIndexImageEnvVar); catalogIndexImage != "" { + if i, ic := DynamicPluginsInitContainer(b.podSpec().InitContainers); i >= 0 && ic != nil { + b.setOrAppendEnvVar(&b.podSpec().InitContainers[i], "CATALOG_INDEX_IMAGE", catalogIndexImage) + } + } + if err := b.setDeployment(backstage); err != nil { return false, err } diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index 1b12b4708..aba03c8b8 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -283,27 +283,10 @@ func (p *DynamicPlugins) getInitContainer() (int, *corev1.Container) { actualInitContainer.Image = os.Getenv(BackstageImageEnvVar) } - // Only set CATALOG_INDEX_IMAGE from operator env var if user hasn't already specified it - // (e.g., via extraEnvs or deployment patch) - if catalogIndexImage := os.Getenv(CatalogIndexImageEnvVar); catalogIndexImage != "" { - if !hasEnvVar(actualInitContainer, "CATALOG_INDEX_IMAGE") { - p.model.backstageDeployment.setOrAppendEnvVar(actualInitContainer, "CATALOG_INDEX_IMAGE", catalogIndexImage) - } - } return i, actualInitContainer } -// hasEnvVar checks if a container already has an environment variable with the given name -func hasEnvVar(container *corev1.Container, name string) bool { - for _, env := range container.Env { - if env.Name == name { - return true - } - } - return false -} - // returns initContainer supposed to initialize DynamicPlugins // TODO consider to use a label to identify instead func DynamicPluginsInitContainer(initContainers []corev1.Container) (int, *corev1.Container) { diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index 38cc25a72..ada04fd35 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -15,6 +15,7 @@ import ( bsv1 "github.com/redhat-developer/rhdh-operator/api/v1alpha5" corev1 "k8s.io/api/core/v1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/stretchr/testify/assert" @@ -279,6 +280,90 @@ func TestCatalogIndexImageEnvVar(t *testing.T) { "CATALOG_INDEX_IMAGE should be set from RELATED_IMAGE_catalog_index") } +// TestCatalogIndexImageOverridesDefaultConfig verifies that RELATED_IMAGE_catalog_index +// overrides the CATALOG_INDEX_IMAGE value that comes from the default config. +// This is the critical test case: the default-config deployment.yaml has CATALOG_INDEX_IMAGE +// set to one value, but RELATED_IMAGE_catalog_index should override it. +func TestCatalogIndexImageOverridesDefaultConfig(t *testing.T) { + bs := testDynamicPluginsBackstage.DeepCopy() + + // janus-deployment.yaml has CATALOG_INDEX_IMAGE set (like the real default-config) + testObj := createBackstageTest(*bs).withDefaultConfig(true). + addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). + addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") + + // Set RELATED_IMAGE_catalog_index to a DIFFERENT value - this should override the default config + t.Setenv(CatalogIndexImageEnvVar, "quay.io/fake-reg/img:1.2.3") + + model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) + assert.NoError(t, err) + assert.NotNil(t, model.backstageDeployment) + + ic := initContainer(model) + assert.NotNil(t, ic) + + // Verify RELATED_IMAGE_catalog_index value is used, NOT the default config value + var catalogIndexValue string + catalogIndexCount := 0 + for _, env := range ic.Env { + if env.Name == "CATALOG_INDEX_IMAGE" { + catalogIndexValue = env.Value + catalogIndexCount++ + } + } + + assert.Equal(t, 1, catalogIndexCount, "CATALOG_INDEX_IMAGE should appear exactly once") + assert.Equal(t, "quay.io/fake-reg/img:1.2.3", catalogIndexValue, + "RELATED_IMAGE_catalog_index should override the default config value") +} + +// TestCatalogIndexImageUserPatchTakesPrecedence verifies that user-specified deployment patch +// takes precedence over the operator's RELATED_IMAGE_catalog_index env var +func TestCatalogIndexImageUserPatchTakesPrecedence(t *testing.T) { + bs := testDynamicPluginsBackstage.DeepCopy() + + // User specifies CATALOG_INDEX_IMAGE via deployment patch + bs.Spec.Deployment = &bsv1.BackstageDeployment{} + bs.Spec.Deployment.Patch = &apiextensionsv1.JSON{ + Raw: []byte(` +spec: + template: + spec: + initContainers: + - name: install-dynamic-plugins + env: + - name: CATALOG_INDEX_IMAGE + value: "quay.io/user-specified/image:2.0" +`), + } + + testObj := createBackstageTest(*bs).withDefaultConfig(true). + addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). + addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") + + // Set RELATED_IMAGE_catalog_index - but user's patch should take precedence + t.Setenv(CatalogIndexImageEnvVar, "quay.io/rhdh/plugin-catalog-index:1.9") + + model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) + assert.NoError(t, err) + assert.NotNil(t, model.backstageDeployment) + + ic := initContainer(model) + assert.NotNil(t, ic) + + // Verify user's patch value takes precedence + var catalogIndexValue string + for _, env := range ic.Env { + if env.Name == "CATALOG_INDEX_IMAGE" { + catalogIndexValue = env.Value + break + } + } + + assert.Equal(t, "quay.io/user-specified/image:2.0", catalogIndexValue, + "user's deployment patch should override RELATED_IMAGE_catalog_index") +} + // TestCatalogIndexImageExtraEnvsOverride verifies that user-specified extraEnvs // takes precedence over the operator's RELATED_IMAGE_catalog_index env var func TestCatalogIndexImageExtraEnvsOverride(t *testing.T) { diff --git a/pkg/model/testdata/janus-deployment.yaml b/pkg/model/testdata/janus-deployment.yaml index ae21b50cc..59f9b792e 100644 --- a/pkg/model/testdata/janus-deployment.yaml +++ b/pkg/model/testdata/janus-deployment.yaml @@ -40,6 +40,9 @@ spec: env: - name: NPM_CONFIG_USERCONFIG value: /opt/app-root/src/.npmrc.dynamic-plugins + # CATALOG_INDEX_IMAGE will be replaced by the value of the `RELATED_IMAGE_catalog_index` env var, if set + - name: CATALOG_INDEX_IMAGE + value: "quay.io/rhdh/plugin-catalog-index:1.9" image: 'quay.io/rhdh/rhdh-hub-rhel9:next' imagePullPolicy: IfNotPresent name: install-dynamic-plugins From fdc93be2051ee3edd1aa434c3a28c1f2a91c84a9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 18 Dec 2025 16:30:44 +0000 Subject: [PATCH 15/32] Regenerate bundle/installer manifests Co-authored-by: Fortune-Ndlovu --- api/v1alpha3/zz_generated.deepcopy.go | 2 +- api/v1alpha4/zz_generated.deepcopy.go | 2 +- api/v1alpha5/zz_generated.deepcopy.go | 2 +- .../manifests/backstage-operator.clusterserviceversion.yaml | 2 +- .../manifests/backstage-operator.clusterserviceversion.yaml | 2 +- bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml | 1 + dist/rhdh/install.yaml | 1 + 7 files changed, 7 insertions(+), 5 deletions(-) diff --git a/api/v1alpha3/zz_generated.deepcopy.go b/api/v1alpha3/zz_generated.deepcopy.go index 6613a8896..b315b789e 100644 --- a/api/v1alpha3/zz_generated.deepcopy.go +++ b/api/v1alpha3/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha3 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha4/zz_generated.deepcopy.go b/api/v1alpha4/zz_generated.deepcopy.go index 9c86c46ee..c9c126a0f 100644 --- a/api/v1alpha4/zz_generated.deepcopy.go +++ b/api/v1alpha4/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha4 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/api/v1alpha5/zz_generated.deepcopy.go b/api/v1alpha5/zz_generated.deepcopy.go index bd2d60544..9e8b769c9 100644 --- a/api/v1alpha5/zz_generated.deepcopy.go +++ b/api/v1alpha5/zz_generated.deepcopy.go @@ -5,7 +5,7 @@ package v1alpha5 import ( - v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) diff --git a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml index 85b4a4159..11645437b 100644 --- a/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/backstage.io/manifests/backstage-operator.clusterserviceversion.yaml @@ -35,7 +35,7 @@ metadata: } } ] - createdAt: "2025-12-10T20:45:39Z" + createdAt: "2025-12-18T16:30:38Z" description: Backstage Operator operators.operatorframework.io/builder: operator-sdk-v1.37.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml index 5432ebb27..610cc193f 100644 --- a/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml +++ b/bundle/rhdh/manifests/backstage-operator.clusterserviceversion.yaml @@ -39,7 +39,7 @@ metadata: categories: Developer Tools certified: "true" containerImage: registry.redhat.io/rhdh/rhdh-rhel9-operator:1.9 - createdAt: "2025-12-10T20:45:42Z" + createdAt: "2025-12-18T16:30:39Z" description: Red Hat Developer Hub is a Red Hat supported version of Backstage. It comes with pre-built plug-ins and configuration settings, supports use of an external database, and can help streamline the process of setting up a self-managed diff --git a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml index 999a30bc6..94e9c064a 100644 --- a/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml +++ b/bundle/rhdh/manifests/rhdh-default-config_v1_configmap.yaml @@ -236,6 +236,7 @@ data: value: /opt/app-root/src/.npmrc.dynamic-plugins/.npmrc - name: MAX_ENTRY_SIZE value: "30000000" + # CATALOG_INDEX_IMAGE will be replaced by the value of the `RELATED_IMAGE_catalog_index` env var, if set - name: CATALOG_INDEX_IMAGE value: "quay.io/rhdh/plugin-catalog-index:1.9" volumeMounts: diff --git a/dist/rhdh/install.yaml b/dist/rhdh/install.yaml index c7588d0f1..ea97ba919 100644 --- a/dist/rhdh/install.yaml +++ b/dist/rhdh/install.yaml @@ -2088,6 +2088,7 @@ data: value: /opt/app-root/src/.npmrc.dynamic-plugins/.npmrc - name: MAX_ENTRY_SIZE value: "30000000" + # CATALOG_INDEX_IMAGE will be replaced by the value of the `RELATED_IMAGE_catalog_index` env var, if set - name: CATALOG_INDEX_IMAGE value: "quay.io/rhdh/plugin-catalog-index:1.9" volumeMounts: From 0f53b2a7fb677ae75be9ac61825678dcc281652e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 09:38:22 +0100 Subject: [PATCH 16/32] fix(deps): update all patch dependencies to v0.34.3 (#1999) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 444f6d33c..3001aa1ed 100644 --- a/go.mod +++ b/go.mod @@ -13,10 +13,10 @@ require ( github.com/tidwall/gjson v1.18.0 golang.org/x/exp v0.0.0-20251209150349-8475f28825e9 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.34.2 - k8s.io/apiextensions-apiserver v0.34.2 - k8s.io/apimachinery v0.34.2 - k8s.io/client-go v0.34.2 + k8s.io/api v0.34.3 + k8s.io/apiextensions-apiserver v0.34.3 + k8s.io/apimachinery v0.34.3 + k8s.io/client-go v0.34.3 k8s.io/klog/v2 v2.130.1 k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 sigs.k8s.io/controller-runtime v0.19.4 From b6608920952e7a4a8bc7550f9f8cfa83ce6c2063 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 17:18:22 +0100 Subject: [PATCH 17/32] chore(deps): update tj-actions/changed-files action to v47.0.1 (#2001) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/next-container-build.yaml | 2 +- .github/workflows/pr-container-build.yaml | 2 +- .github/workflows/pr.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/next-container-build.yaml b/.github/workflows/next-container-build.yaml index 39a95f0f1..508db5ff1 100644 --- a/.github/workflows/next-container-build.yaml +++ b/.github/workflows/next-container-build.yaml @@ -32,7 +32,7 @@ jobs: # check changes in this commit for regex include and exclude matches - name: Get changed files id: changed-files - uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0 + uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 with: files: | .github/workflows/next-container-build.yaml diff --git a/.github/workflows/pr-container-build.yaml b/.github/workflows/pr-container-build.yaml index 0a783f420..8df7c912a 100644 --- a/.github/workflows/pr-container-build.yaml +++ b/.github/workflows/pr-container-build.yaml @@ -55,7 +55,7 @@ jobs: # check changes in this commit for regex include and exclude matches - name: Get changed files id: changed-files - uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0 + uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 with: files: | .github/workflows/pr-container-build.yaml diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index ddfc27c29..ee65431e9 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -37,7 +37,7 @@ jobs: # check changes in this commit for regex include and exclude matches - name: Get changed files id: changed-files - uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47.0.0 + uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1 with: files: | .github/workflows/pr.yaml From 7a5ca5b0cc9bf365151b60f69b664a183e39569c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:01:47 +0100 Subject: [PATCH 18/32] chore(deps): update redhat-plumbers-in-action/differential-shellcheck digest to d965e66 (#2003) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/scripts-checks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scripts-checks.yaml b/.github/workflows/scripts-checks.yaml index fa9b1c629..be67285ad 100644 --- a/.github/workflows/scripts-checks.yaml +++ b/.github/workflows/scripts-checks.yaml @@ -31,7 +31,7 @@ jobs: - id: ShellCheck name: Differential ShellCheck - uses: redhat-plumbers-in-action/differential-shellcheck@0d9e5b29625f871e6a4215380486d6f1a7cb6cdd # v5 + uses: redhat-plumbers-in-action/differential-shellcheck@d965e66ec0b3b2f821f75c8eff9b12442d9a7d1e # v5 with: token: ${{ secrets.GITHUB_TOKEN }} From d1f66d9a339b6f1a40e5e59a5160767dcd0bfb07 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Dec 2025 13:02:14 +0100 Subject: [PATCH 19/32] chore(deps): update github/codeql-action digest to 1b168cd (#2006) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index ee65431e9..3aedc1600 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -114,7 +114,7 @@ jobs: - name: Upload SARIF file if: steps.changed-files.outputs.any_changed == 'true' - uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4 + uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4 with: # Path to SARIF file relative to the root of the repository sarif_file: gosec.sarif From 987596d986dafd97fba04ff649911572dc997eec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 13 Dec 2025 21:15:25 +0100 Subject: [PATCH 20/32] fix(deps): update github.com/openshift/api digest to cafbd60 (#2009) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3001aa1ed..4bdb3dc04 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.24.6 require ( github.com/onsi/ginkgo/v2 v2.22.2 github.com/onsi/gomega v1.36.2 - github.com/openshift/api v0.0.0-20251208101024-c2a41ea924bd + github.com/openshift/api v0.0.0-20251212171319-cafbd603d60d github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.71.2 github.com/stretchr/testify v1.10.0 github.com/tidwall/gjson v1.18.0 diff --git a/go.sum b/go.sum index 716625dfe..74b192918 100644 --- a/go.sum +++ b/go.sum @@ -110,8 +110,8 @@ github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= -github.com/openshift/api v0.0.0-20251208101024-c2a41ea924bd h1:dVvuq3+RR7yEI2M8qKZqc0grAcNHog5hM2CQYZe5T3U= -github.com/openshift/api v0.0.0-20251208101024-c2a41ea924bd/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY= +github.com/openshift/api v0.0.0-20251212171319-cafbd603d60d h1:gu/8Xn37tkVgDSOM7Uxtk7M0oO4+9s2IbzHGd7yWmLg= +github.com/openshift/api v0.0.0-20251212171319-cafbd603d60d/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= From d5a51bdd9c067127df4f9aa2f9d297348787984b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 13 Dec 2025 21:16:41 +0100 Subject: [PATCH 21/32] chore(deps): update actions/upload-artifact action to v6 (#2011) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/scripts-checks.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/scripts-checks.yaml b/.github/workflows/scripts-checks.yaml index be67285ad..66e19af41 100644 --- a/.github/workflows/scripts-checks.yaml +++ b/.github/workflows/scripts-checks.yaml @@ -37,7 +37,7 @@ jobs: - if: always() name: Upload artifact with ShellCheck defects in SARIF format - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: Differential ShellCheck SARIF path: ${{ steps.ShellCheck.outputs.sarif }} From 46e02e62613673e68d963de7daaf1dcf087149a5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 14 Dec 2025 16:32:03 +0100 Subject: [PATCH 22/32] fix(deps): update github.com/openshift/api digest to bfa868a (#2015) Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4bdb3dc04..ebdcff19c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.24.6 require ( github.com/onsi/ginkgo/v2 v2.22.2 github.com/onsi/gomega v1.36.2 - github.com/openshift/api v0.0.0-20251212171319-cafbd603d60d + github.com/openshift/api v0.0.0-20251214014457-bfa868a22401 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.71.2 github.com/stretchr/testify v1.10.0 github.com/tidwall/gjson v1.18.0 diff --git a/go.sum b/go.sum index 74b192918..a36207842 100644 --- a/go.sum +++ b/go.sum @@ -110,8 +110,8 @@ github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= -github.com/openshift/api v0.0.0-20251212171319-cafbd603d60d h1:gu/8Xn37tkVgDSOM7Uxtk7M0oO4+9s2IbzHGd7yWmLg= -github.com/openshift/api v0.0.0-20251212171319-cafbd603d60d/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY= +github.com/openshift/api v0.0.0-20251214014457-bfa868a22401 h1:goMf6pBtRFSQaVElFk6K+GIAqnv7O84p7PJHH6pDz/E= +github.com/openshift/api v0.0.0-20251214014457-bfa868a22401/go.mod h1:d5uzF0YN2nQQFA0jIEWzzOZ+edmo6wzlGLvx5Fhz4uY= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= From ef549acd2044fa5fe9f607dac29513de7b522de3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 23:06:35 +0100 Subject: [PATCH 23/32] chore(deps): update github/codeql-action digest to 5d4e8d1 (#2018) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/pr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 3aedc1600..51a710ecb 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -114,7 +114,7 @@ jobs: - name: Upload SARIF file if: steps.changed-files.outputs.any_changed == 'true' - uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v4 + uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4 with: # Path to SARIF file relative to the root of the repository sarif_file: gosec.sarif From c789fd48de9ccd12a90d036d399b60f9028c7501 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 23:48:46 +0100 Subject: [PATCH 24/32] fix(deps): update k8s.io/utils digest to 61b37f7 (#2021) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ebdcff19c..fe38ba511 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( k8s.io/apimachinery v0.34.3 k8s.io/client-go v0.34.3 k8s.io/klog/v2 v2.130.1 - k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 + k8s.io/utils v0.0.0-20251218160917-61b37f7a4624 sigs.k8s.io/controller-runtime v0.19.4 sigs.k8s.io/kustomize/kyaml v0.18.1 sigs.k8s.io/yaml v1.6.0 diff --git a/go.sum b/go.sum index a36207842..ec8198311 100644 --- a/go.sum +++ b/go.sum @@ -266,8 +266,8 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20251218160917-61b37f7a4624 h1:wadElzGW3vTZ1Et18CImPEErLaXvMSU5369b0to32+0= +k8s.io/utils v0.0.0-20251218160917-61b37f7a4624/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.19.4 h1:SUmheabttt0nx8uJtoII4oIP27BVVvAKFvdvGFwV/Qo= From 79d7c2d24b92c08fdad43c6f68aab055d16ff81b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 08:34:09 +0100 Subject: [PATCH 25/32] chore(deps): update registry.access.redhat.com/ubi9/go-toolset docker tag to v9.7-1766073541 (#2023) Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .rhdh/docker/Dockerfile | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.rhdh/docker/Dockerfile b/.rhdh/docker/Dockerfile index e7aee24ca..ca3d334e4 100644 --- a/.rhdh/docker/Dockerfile +++ b/.rhdh/docker/Dockerfile @@ -2,7 +2,7 @@ #@follow_tag(registry.redhat.io/rhel9/go-toolset:latest) # https://registry.access.redhat.com/ubi9/go-toolset -FROM registry.access.redhat.com/ubi9/go-toolset:9.7-1765311584@sha256:56fc8480721e5febbd2b6810199a63b5b68c3c5ab826d082c8247054a1dc33e7 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:9.7-1766073541@sha256:401242712015b758ebb6f26e74e66da11e9b8762e1d92ad7696c76a10ea0aaf0 AS builder ARG TARGETOS ARG TARGETARCH # hadolint ignore=DL3002 diff --git a/Dockerfile b/Dockerfile index 7fa9f2a31..e07b03248 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ #@follow_tag(registry.redhat.io/rhel9/go-toolset:latest) # https://registry.access.redhat.com/ubi9/go-toolset -FROM registry.access.redhat.com/ubi9/go-toolset:9.7-1765311584@sha256:56fc8480721e5febbd2b6810199a63b5b68c3c5ab826d082c8247054a1dc33e7 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:9.7-1766073541@sha256:401242712015b758ebb6f26e74e66da11e9b8762e1d92ad7696c76a10ea0aaf0 AS builder ARG TARGETOS ARG TARGETARCH # hadolint ignore=DL3002 From c459e8bf820703a6ca49239494e17c5ac26a3269 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 23 Dec 2025 16:19:10 +0100 Subject: [PATCH 26/32] Apply suggestions from code review --- pkg/model/deployment.go | 2 +- pkg/model/dynamic-plugins.go | 2 +- pkg/model/dynamic-plugins_test.go | 80 +++++++++---------------------- 3 files changed, 25 insertions(+), 59 deletions(-) diff --git a/pkg/model/deployment.go b/pkg/model/deployment.go index 028610970..e4e6d747f 100644 --- a/pkg/model/deployment.go +++ b/pkg/model/deployment.go @@ -114,7 +114,7 @@ func (b *BackstageDeployment) addToModel(model *BackstageModel, backstage bsv1.B // Set CATALOG_INDEX_IMAGE from operator env var BEFORE extraEnvs are applied, so user-specified extraEnvs can still override this value if catalogIndexImage := os.Getenv(CatalogIndexImageEnvVar); catalogIndexImage != "" { - if i, ic := DynamicPluginsInitContainer(b.podSpec().InitContainers); i >= 0 && ic != nil { + if i, _ := DynamicPluginsInitContainer(b.podSpec().InitContainers); i >= 0 { b.setOrAppendEnvVar(&b.podSpec().InitContainers[i], "CATALOG_INDEX_IMAGE", catalogIndexImage) } } diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index aba03c8b8..a1b374aa6 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -284,7 +284,7 @@ func (p *DynamicPlugins) getInitContainer() (int, *corev1.Container) { } - return i, actualInitContainer + return i, initContainer } // returns initContainer supposed to initialize DynamicPlugins diff --git a/pkg/model/dynamic-plugins_test.go b/pkg/model/dynamic-plugins_test.go index ada04fd35..22ec4f8c2 100644 --- a/pkg/model/dynamic-plugins_test.go +++ b/pkg/model/dynamic-plugins_test.go @@ -248,18 +248,16 @@ func initContainer(model *BackstageModel) *corev1.Container { return nil } -// TestCatalogIndexImageEnvVar verifies that the operator reads RELATED_IMAGE_catalog_index -// and sets CATALOG_INDEX_IMAGE on the install-dynamic-plugins init container -func TestCatalogIndexImageEnvVar(t *testing.T) { + +// TestCatalogIndexImageFromDefaultConfig verifies that the operator sets CATALOG_INDEX_IMAGE +// on the install-dynamic-plugins init container from the default config by default +func TestCatalogIndexImageFromDefaultConfig(t *testing.T) { bs := testDynamicPluginsBackstage.DeepCopy() testObj := createBackstageTest(*bs).withDefaultConfig(true). addToDefaultConfig("dynamic-plugins.yaml", "raw-dynamic-plugins.yaml"). addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") - // Set the RELATED_IMAGE_catalog_index env var (simulating operator environment) - t.Setenv(CatalogIndexImageEnvVar, "quay.io/rhdh/plugin-catalog-index:1.9") - model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) assert.NotNil(t, model.backstageDeployment) @@ -267,17 +265,10 @@ func TestCatalogIndexImageEnvVar(t *testing.T) { ic := initContainer(model) assert.NotNil(t, ic) - // Verify CATALOG_INDEX_IMAGE is set on the init container - var catalogIndexValue string - for _, env := range ic.Env { - if env.Name == "CATALOG_INDEX_IMAGE" { - catalogIndexValue = env.Value - break - } - } - - assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.9", catalogIndexValue, - "CATALOG_INDEX_IMAGE should be set from RELATED_IMAGE_catalog_index") + assert.Len(t, ic.Env, 2) + assert.Equal(t, "NPM_CONFIG_USERCONFIG", ic.Env[0].Name) + assert.Equal(t, "CATALOG_INDEX_IMAGE", ic.Env[1].Name) + assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.9", ic.Env[1].Value, "CATALOG_INDEX_IMAGE should be set from the default config") } // TestCatalogIndexImageOverridesDefaultConfig verifies that RELATED_IMAGE_catalog_index @@ -302,19 +293,10 @@ func TestCatalogIndexImageOverridesDefaultConfig(t *testing.T) { ic := initContainer(model) assert.NotNil(t, ic) - // Verify RELATED_IMAGE_catalog_index value is used, NOT the default config value - var catalogIndexValue string - catalogIndexCount := 0 - for _, env := range ic.Env { - if env.Name == "CATALOG_INDEX_IMAGE" { - catalogIndexValue = env.Value - catalogIndexCount++ - } - } - - assert.Equal(t, 1, catalogIndexCount, "CATALOG_INDEX_IMAGE should appear exactly once") - assert.Equal(t, "quay.io/fake-reg/img:1.2.3", catalogIndexValue, - "RELATED_IMAGE_catalog_index should override the default config value") + assert.Len(t, ic.Env, 2) + assert.Equal(t, "NPM_CONFIG_USERCONFIG", ic.Env[0].Name) + assert.Equal(t, "CATALOG_INDEX_IMAGE", ic.Env[1].Name) + assert.Equal(t, "quay.io/fake-reg/img:1.2.3", ic.Env[1].Value, "RELATED_IMAGE_catalog_index should override the default config value") } // TestCatalogIndexImageUserPatchTakesPrecedence verifies that user-specified deployment patch @@ -342,7 +324,7 @@ spec: addToDefaultConfig("deployment.yaml", "janus-deployment.yaml") // Set RELATED_IMAGE_catalog_index - but user's patch should take precedence - t.Setenv(CatalogIndexImageEnvVar, "quay.io/rhdh/plugin-catalog-index:1.9") + t.Setenv(CatalogIndexImageEnvVar, "quay.io/rhdh/plugin-catalog-index:related-image") model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) @@ -350,18 +332,10 @@ spec: ic := initContainer(model) assert.NotNil(t, ic) - - // Verify user's patch value takes precedence - var catalogIndexValue string - for _, env := range ic.Env { - if env.Name == "CATALOG_INDEX_IMAGE" { - catalogIndexValue = env.Value - break - } - } - - assert.Equal(t, "quay.io/user-specified/image:2.0", catalogIndexValue, - "user's deployment patch should override RELATED_IMAGE_catalog_index") + assert.Len(t, ic.Env, 2) + assert.Equal(t, "NPM_CONFIG_USERCONFIG", ic.Env[0].Name) + assert.Equal(t, "CATALOG_INDEX_IMAGE", ic.Env[1].Name) + assert.Equal(t, "quay.io/user-specified/image:2.0", ic.Env[1].Value, "user's deployment patch should override RELATED_IMAGE_catalog_index") } // TestCatalogIndexImageExtraEnvsOverride verifies that user-specified extraEnvs @@ -372,7 +346,7 @@ func TestCatalogIndexImageExtraEnvsOverride(t *testing.T) { // User specifies a different catalog index image via extraEnvs bs.Spec.Application.ExtraEnvs = &bsv1.ExtraEnvs{ Envs: []bsv1.Env{ - {Name: "CATALOG_INDEX_IMAGE", Value: "quay.io/rhdh/plugin-catalog-index:1.8", Containers: []string{"install-dynamic-plugins"}}, + {Name: "CATALOG_INDEX_IMAGE", Value: "quay.io/rhdh/plugin-catalog-index:extra-env", Containers: []string{"install-dynamic-plugins"}}, }, } @@ -382,7 +356,7 @@ func TestCatalogIndexImageExtraEnvsOverride(t *testing.T) { // Set the RELATED_IMAGE_catalog_index env var (simulating operator environment) // This should NOT override the user's extraEnvs value - t.Setenv(CatalogIndexImageEnvVar, "quay.io/rhdh/plugin-catalog-index:1.9") + t.Setenv(CatalogIndexImageEnvVar, "quay.io/rhdh/plugin-catalog-index:related-image") model, err := InitObjects(context.TODO(), *bs, testObj.externalConfig, platform.Default, testObj.scheme) assert.NoError(t, err) @@ -390,18 +364,10 @@ func TestCatalogIndexImageExtraEnvsOverride(t *testing.T) { ic := initContainer(model) assert.NotNil(t, ic) - - // Verify user's extraEnvs value takes precedence - var catalogIndexValue string - for _, env := range ic.Env { - if env.Name == "CATALOG_INDEX_IMAGE" { - catalogIndexValue = env.Value - break - } - } - - assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.8", catalogIndexValue, - "extraEnvs value should override the operator's RELATED_IMAGE_catalog_index") + assert.Len(t, ic.Env, 2) + assert.Equal(t, "NPM_CONFIG_USERCONFIG", ic.Env[0].Name) + assert.Equal(t, "CATALOG_INDEX_IMAGE", ic.Env[1].Name) + assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:extra-env", ic.Env[1].Value, "extraEnvs value should override the operator's RELATED_IMAGE_catalog_index") } func TestUnmarshalDynaPluginsConfig(t *testing.T) { From 521c74a324d7907089ad2996c6e8a2559da15c52 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 23 Dec 2025 16:21:56 +0100 Subject: [PATCH 27/32] Update pkg/model/deployment_test.go --- pkg/model/deployment_test.go | 57 ------------------------------------ 1 file changed, 57 deletions(-) diff --git a/pkg/model/deployment_test.go b/pkg/model/deployment_test.go index 651db925f..2c01334b2 100644 --- a/pkg/model/deployment_test.go +++ b/pkg/model/deployment_test.go @@ -271,63 +271,6 @@ spec: } -// TestExtraEnvsReplacesExistingEnvVar verifies that when a user specifies an env var -// via extraEnvs that already exists in the default config, it replaces the existing -// value instead of creating duplicates (which would cause Kubernetes to reject the deployment) -func TestExtraEnvsReplacesExistingEnvVar(t *testing.T) { - bs := *deploymentTestBackstage.DeepCopy() - - // Add an env var via patch that will already exist in the deployment - bs.Spec.Deployment = &bsv1.BackstageDeployment{} - bs.Spec.Deployment.Patch = &apiextensionsv1.JSON{ - Raw: []byte(` -spec: - template: - spec: - initContainers: - - name: install-dynamic-plugins - env: - - name: CATALOG_INDEX_IMAGE - value: "quay.io/rhdh/plugin-catalog-index:1.8" -`), - } - - // Now specify the same env var via extraEnvs with a different value (upgrading to latest) - bs.Spec.Application.ExtraEnvs = &bsv1.ExtraEnvs{ - Envs: []bsv1.Env{ - {Name: "CATALOG_INDEX_IMAGE", Value: "quay.io/rhdh/plugin-catalog-index:1.9", Containers: []string{"install-dynamic-plugins"}}, - }, - } - - testObj := createBackstageTest(bs).withDefaultConfig(true) - - model, err := InitObjects(context.TODO(), bs, testObj.externalConfig, platform.Default, testObj.scheme) - assert.NoError(t, err) - assert.NotNil(t, model.backstageDeployment) - - // Find the init container - var initContainerEnvs []corev1.EnvVar - for _, ic := range model.backstageDeployment.podSpec().InitContainers { - if ic.Name == "install-dynamic-plugins" { - initContainerEnvs = ic.Env - break - } - } - - // Verify there's no duplicate - only ONE CATALOG_INDEX_IMAGE env var should exist - catalogIndexCount := 0 - var catalogIndexValue string - for _, env := range initContainerEnvs { - if env.Name == "CATALOG_INDEX_IMAGE" { - catalogIndexCount++ - catalogIndexValue = env.Value - } - } - - assert.Equal(t, 1, catalogIndexCount, "CATALOG_INDEX_IMAGE should appear exactly once, not duplicated") - assert.Equal(t, "quay.io/rhdh/plugin-catalog-index:1.9", catalogIndexValue, "extraEnvs value should override the default") -} - func TestDeploymentKind(t *testing.T) { bs := *deploymentTestBackstage.DeepCopy() From c6ab3e30e3d0b5fef2ac15efb12c7b7534b86532 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 23 Dec 2025 16:22:09 +0100 Subject: [PATCH 28/32] Update pkg/model/dynamic-plugins.go --- pkg/model/dynamic-plugins.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index a1b374aa6..b2a03cb93 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -280,7 +280,7 @@ func (p *DynamicPlugins) getInitContainer() (int, *corev1.Container) { // override image with env var if os.Getenv(BackstageImageEnvVar) != "" { - actualInitContainer.Image = os.Getenv(BackstageImageEnvVar) + initContainer.Image = os.Getenv(BackstageImageEnvVar) } From f058548e5329506b3923b668580aa8470cb44b0a Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 23 Dec 2025 16:22:19 +0100 Subject: [PATCH 29/32] Update pkg/model/dynamic-plugins.go --- pkg/model/dynamic-plugins.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index b2a03cb93..2ee7cbc2c 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -273,10 +273,6 @@ func (p *DynamicPlugins) mergeWith(specData string) (string, error) { } func (p *DynamicPlugins) getInitContainer() (int, *corev1.Container) { i, initContainer := DynamicPluginsInitContainer(p.model.backstageDeployment.podSpec().InitContainers) - if i < 0 || initContainer == nil { - return i, initContainer - } - actualInitContainer := &p.model.backstageDeployment.podSpec().InitContainers[i] // override image with env var if os.Getenv(BackstageImageEnvVar) != "" { From b1db6659030f11cafb5408ab8096bb89566b1db8 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 23 Dec 2025 16:24:50 +0100 Subject: [PATCH 30/32] Update pkg/model/deployment_test.go --- pkg/model/deployment_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/model/deployment_test.go b/pkg/model/deployment_test.go index 2c01334b2..6b2ea8266 100644 --- a/pkg/model/deployment_test.go +++ b/pkg/model/deployment_test.go @@ -7,7 +7,6 @@ import ( "github.com/redhat-developer/rhdh-operator/pkg/platform" appv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/utils/ptr" From 7a12dfff5f5cf80354e48939fc8f8e19e6972c3b Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 23 Dec 2025 16:25:01 +0100 Subject: [PATCH 31/32] Update pkg/model/dynamic-plugins.go --- pkg/model/dynamic-plugins.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index 2ee7cbc2c..32047c0b1 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -279,7 +279,6 @@ func (p *DynamicPlugins) getInitContainer() (int, *corev1.Container) { initContainer.Image = os.Getenv(BackstageImageEnvVar) } - return i, initContainer } From 336a136e941d0a3da27300e9680d0817cc2e4a10 Mon Sep 17 00:00:00 2001 From: Armel Soro Date: Tue, 23 Dec 2025 16:26:30 +0100 Subject: [PATCH 32/32] Apply suggestion from @rm3l --- pkg/model/dynamic-plugins.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/model/dynamic-plugins.go b/pkg/model/dynamic-plugins.go index 32047c0b1..ffcb6c9fa 100644 --- a/pkg/model/dynamic-plugins.go +++ b/pkg/model/dynamic-plugins.go @@ -278,7 +278,6 @@ func (p *DynamicPlugins) getInitContainer() (int, *corev1.Container) { if os.Getenv(BackstageImageEnvVar) != "" { initContainer.Image = os.Getenv(BackstageImageEnvVar) } - return i, initContainer }