Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
bin
dist
.tools
manager
setup-shared-storage

# Test binary, built with `go test -c`
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ BUNDLE_IMG ?= $(QUAY_OPERATOR_NAME)-bundle:$(IMAGE_TAG)
CATALOG_IMG ?= $(QUAY_OPERATOR_NAME)-catalog:$(IMAGE_TAG)

AGENT_IMG ?= $(IMAGE_REGISTRY)/$(AGENT_NAME):$(IMAGE_TAG)
export AGENT_IMG

OPERATOR_SHA=$$(podman inspect $(QUAY_OPERATOR_NAME):$(IMAGE_TAG) --format "{{.ID}}" )
AGENT_SHA=$$(podman inspect $(QUAY_AGENT_IMG):$(IMAGE_TAG) --format "{{.ID}}" )
Expand Down Expand Up @@ -669,7 +670,7 @@ CSV ?= ./bundle/manifests/$(OPERATOR_NAME).clusterserviceversion.yaml
.PHONY: bundle
bundle: manifests operator-sdk kustomize yq ## Generate OLM bundle manifests and metadata, then validate
cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
$(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle -q --manifests --metadata --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
$(KUSTOMIZE) build config/manifests | envsubst '$$AGENT_IMG' | $(OPERATOR_SDK) generate bundle -q --manifests --metadata --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
$(MAKE) bundle-validate

.PHONY: bundle-validate
Expand Down
64 changes: 2 additions & 62 deletions api/v1alpha1/storagebasedremediationconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,14 @@ package v1alpha1

import (
"fmt"
"os"
"strings"
"time"
"unicode"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
logf "sigs.k8s.io/controller-runtime/pkg/log"

"github.com/medik8s/storage-based-remediation/internal/agent"
)

var typesLog = logf.Log.WithName("sbrconfig-types")

// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

Expand All @@ -42,8 +37,8 @@ const (
DefaultSBRTimeoutSeconds = 30
// DefaultMaxConsecutiveFailures is the runtime default when maxConsecutiveFailures is unset on the CR (no OpenAPI default).
DefaultMaxConsecutiveFailures = 7
// RelatedImageSbrAgent when this env is set it contains the image of SBR agent
RelatedImageSbrAgent = "RELATED_IMAGE_SBR_AGENT"
// RelatedImageAgent when this env is set it contains the image of SBR agent
RelatedImageAgent = "RELATED_IMAGE_AGENT"
)

// DetectOnlyModeType specifies whether SBR runs in detect-only mode (no remediation).
Expand Down Expand Up @@ -281,61 +276,6 @@ func (s *StorageBasedRemediationConfigSpec) ValidateAll() error {
return nil
}

// DeriveAgentImageFromOperator derives the sbr-agent image from the operator image
func DeriveAgentImageFromOperator(operatorImage string) (string, error) {
// Handle empty operator image
if operatorImage == "" {
return "", fmt.Errorf("invalid empty operator image")
}

// In CI, RELATED_IMAGE_SBR_AGENT is injected via `oc set env` after bundle installation,
// pointing to the CI-built agent image. In non-CI runs this env var is not set,
// so we fall through to pod image discovery and derivation.
if img, found := os.LookupEnv(RelatedImageSbrAgent); found {
typesLog.Info("Using RELATED_IMAGE_SBR_AGENT for agent image", "image", img)
return img, nil
}

lastSlash := strings.LastIndex(operatorImage, "/")
var prefix, suffix string
if lastSlash == -1 {
prefix = ""
suffix = operatorImage
} else {
prefix = operatorImage[:lastSlash+1]
suffix = operatorImage[lastSlash+1:]
}

// If already an agent image (e.g. controller fallback in tests), return as-is
if suffix == "sbr-agent" || strings.HasPrefix(suffix, "sbr-agent:") {
agentSuffix := suffix
if !strings.Contains(agentSuffix, ":") {
agentSuffix += ":latest"
}
return prefix + agentSuffix, nil
}

// Replace operator with agent in the image name. Two naming schemes are supported:
// 1) storage-based-remediation-*-operator -> storage-based-remediation-agent-* (then strip "-operator")
// 2) sbr-operator -> sbr-agent
agentSuffix := strings.Replace(suffix, "storage-based-remediation", "storage-based-remediation-agent", 1)
if agentSuffix != suffix {
agentSuffix = strings.Replace(agentSuffix, "-operator", "", 1)
} else {
agentSuffix = strings.Replace(suffix, "sbr-operator", "sbr-agent", 1)
}
if agentSuffix == suffix {
return "", fmt.Errorf("invalid operator image %q", operatorImage)
}

// Add :latest tag if no tag is present
if !strings.Contains(agentSuffix, ":") {
agentSuffix += ":latest"
}

return prefix + agentSuffix, nil
}

// StorageBasedRemediationConfigStatus defines the observed state of StorageBasedRemediationConfig.
type StorageBasedRemediationConfigStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
Expand Down
69 changes: 0 additions & 69 deletions api/v1alpha1/storagebasedremediationconfig_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,75 +336,6 @@ func TestWatchdogConstants(t *testing.T) {
}
}

func TestDeriveAgentImageFromOperator(t *testing.T) {
tests := []struct {
name string
operatorImage string
expected string
wantErr bool
}{
{
name: "derive from operator image with tag",
operatorImage: "quay.io/medik8s/sbr-operator:v1.2.3",
expected: "quay.io/medik8s/sbr-agent:v1.2.3",
},
{
name: "derive from operator image without tag",
operatorImage: "quay.io/medik8s/sbr-operator",
expected: "quay.io/medik8s/sbr-agent:latest",
},
{
name: "simple operator image with tag",
operatorImage: "sbr-operator:v1.0.0",
expected: "sbr-agent:v1.0.0",
},
{
name: "simple operator image without tag",
operatorImage: "sbr-operator",
expected: "sbr-agent:latest",
},
{
name: "empty operator image",
operatorImage: "",
wantErr: true,
},
{
name: "complex registry path",
operatorImage: "registry.example.com:5000/my-org/my-project/sbr-operator:dev-123",
expected: "registry.example.com:5000/my-org/my-project/sbr-agent:dev-123",
},
{
name: "storage-based-remediation operator image (RH naming)",
operatorImage: "registry.redhat.io/workload-availability/storage-based-remediation-rhel9-operator:v0.1.0",
expected: "registry.redhat.io/workload-availability/storage-based-remediation-agent-rhel9:v0.1.0",
},
{
name: "already agent image (e.g. controller fallback)",
operatorImage: "sbr-agent:latest",
expected: "sbr-agent:latest",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := DeriveAgentImageFromOperator(tt.operatorImage)
if tt.wantErr {
if err == nil {
t.Errorf("DeriveAgentImageFromOperator() expected error for operator image %q", tt.operatorImage)
}
return
}
if err != nil {
t.Errorf("DeriveAgentImageFromOperator() unexpected error: %v", err)
return
}
if result != tt.expected {
t.Errorf("DeriveAgentImageFromOperator() = %v, expected %v", result, tt.expected)
}
})
}
}

func TestStorageBasedRemediationConfigSpec_GetSharedStoragePVCName(t *testing.T) {
tests := []struct {
name string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,8 @@ spec:
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: RELATED_IMAGE_AGENT
value: quay.io/medik8s/storage-based-remediation-agent:latest
image: quay.io/medik8s/storage-based-remediation-operator:latest
livenessProbe:
httpGet:
Expand Down Expand Up @@ -414,4 +416,7 @@ spec:
provider:
name: medik8s
url: https://github.com/medik8s
relatedImages:
- image: quay.io/medik8s/storage-based-remediation-agent:latest
name: agent
version: 0.0.1
12 changes: 10 additions & 2 deletions config/manager/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,13 @@ images:
- name: controller
newName: quay.io/medik8s/storage-based-remediation-operator
newTag: latest
- name: imagePullPolicy
newName: Never
patches:
- patch: |
- op: add
path: /spec/template/spec/containers/0/env/-
value:
name: RELATED_IMAGE_AGENT
value: ${AGENT_IMG}
target:
kind: Deployment
name: controller-manager
Comment on lines +9 to +18
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Confirm placeholder injection in manager kustomization"
rg -n '\$\{AGENT_IMG\}' config/manager/kustomization.yaml

echo
echo "2) Confirm non-bundle kustomize paths currently skip envsubst"
rg -n '^\s*\$\(KUSTOMIZE\) build config/default( \|| >)' Makefile -C1
rg -n '^\s*\$\(KUSTOMIZE\) build config/openshift-default >' Makefile -C1

echo
echo "3) If kustomize exists, check rendered default manifest for literal placeholder"
if command -v kustomize >/dev/null 2>&1; then
  kustomize build config/default | rg -n 'RELATED_IMAGE_AGENT|\$\{AGENT_IMG\}' -C1
else
  echo "kustomize not found on PATH; install it and rerun step 3."
fi

Repository: medik8s/storage-based-remediation

Length of output: 1142


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== config/manager/kustomization.yaml (context around injected placeholder) =="
nl -ba config/manager/kustomization.yaml | sed -n '1,120p'

echo
echo "== Where envsubst is used (repo-wide) =="
rg -n "envsubst" -S . || true

echo
echo "== Where AGENT_IMG is referenced (repo-wide) =="
rg -n "AGENT_IMG" -S . || true

echo
echo "== KUSTOMIZE build/apply pipelines in Makefile that might render manifests =="
rg -n "\\$\\(KUSTOMIZE\\) build|kustomize build" Makefile -S || true

Repository: medik8s/storage-based-remediation

Length of output: 216


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== config/manager/kustomization.yaml (first 160 lines) =="
cat -n config/manager/kustomization.yaml | sed -n '1,160p'

echo
echo "== Where envsubst is used (repo-wide) =="
rg -n "envsubst" -S . || true

echo
echo "== Where AGENT_IMG is referenced (repo-wide) =="
rg -n "AGENT_IMG" -S . || true

echo
echo "== Kustomize build/apply pipelines in Makefile(s) =="
rg -n "\\$\\(KUSTOMIZE\\) build|kustomize build|\\$\\(KUBECTL\\) apply|\\$\\(KUBECTL\\) delete" Makefile* -S || true
ls -la

Repository: medik8s/storage-based-remediation

Length of output: 6658


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Makefile: KUSTOMIZE definition =="
rg -n "^[A-Z_]+\\s*\\??=\\s*.*kustomize" Makefile
rg -n "KUSTOMIZE\\s*[:?]?=" Makefile

echo
echo "== Makefile: relevant targets around build/install/deploy =="
rg -n "^build-installer:|^build-openshift-installer:|^deploy:|^undeploy:|^bundle|generate bundle|OPERATOR_SDK generate bundle" Makefile

echo
echo "== Show Makefile sections around those matches =="
# Use awk to print ~40 lines around each match line number
python3 - <<'PY'
import re, subprocess, os, sys
import pathlib

mk = pathlib.Path("Makefile").read_text().splitlines()
pat = re.compile(r'^(build-installer:|build-openshift-installer:|deploy:|undeploy:|bundle|generate bundle|OPERATOR_SDK generate bundle)')
for i,line in enumerate(mk, start=1):
    if pat.search(line):
        start=max(1,i-25); end=min(len(mk), i+35)
        print("\n\n--- Makefile lines", start, "-", end, "(around", i, ")", "---")
        for j in range(start,end+1):
            print(f"{j:5d}\t{mk[j-1]}")
PY

Repository: medik8s/storage-based-remediation

Length of output: 43375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== kustomization.yaml files under config/manager =="
find config/manager -maxdepth 6 -name "kustomization.yaml" -print | sort

echo
echo "== Inspect config/manager/config/default/kustomization.yaml (if present) =="
if [ -f config/manager/config/default/kustomization.yaml ]; then
  cat -n config/manager/config/default/kustomization.yaml
else
  echo "MISSING: config/manager/config/default/kustomization.yaml"
fi

echo
echo "== Inspect config/manager/manager.yaml (referenced by root kustomization.yaml) =="
if [ -f config/manager/manager.yaml ]; then
  rg -n "RELATED_IMAGE_AGENT|AGENT_IMG|\$\{AGENT_IMG\}" config/manager/manager.yaml || true
  cat -n config/manager/manager.yaml | sed -n '1,220p'
else
  echo "MISSING: config/manager/manager.yaml"
fi

echo
echo "== Find RELATED_IMAGE_AGENT usage across config/manager =="
rg -n "RELATED_IMAGE_AGENT|\$\{AGENT_IMG\}" config/manager || true

Repository: medik8s/storage-based-remediation

Length of output: 5212


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Directory layout under config/manager =="
ls -la config/manager
echo
find config/manager -maxdepth 3 -type d -print | sort

echo
echo "== Any kustomization.yaml under config/manager (any depth) =="
find config/manager -type f -name "kustomization.yaml" -print | sort

echo
echo "== Check for expected overlay directories =="
for d in config/manager/config/default config/manager/config/openshift-default config/manager/config/manifests config/manager/default config/manager/openshift-default config/manager/manifests; do
  if [ -d "$d" ]; then
    echo "FOUND: $d"
    ls -la "$d"
  else
    echo "MISSING: $d"
  fi
done

echo
echo "== Show first 160 lines of config/manager/kustomization.yaml (root) =="
cat -n config/manager/kustomization.yaml | sed -n '1,200p' || true

Repository: medik8s/storage-based-remediation

Length of output: 1376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find update-manifests target =="
rg -n "^update-manifests:|update-manifests" Makefile

echo
echo "== Show Makefile section around update-manifests =="
python3 - <<'PY'
import pathlib, re
mk = pathlib.Path("Makefile").read_text().splitlines()
for i,l in enumerate(mk, start=1):
    if re.match(r"^update-manifests:", l):
        start=max(1,i-30); end=min(len(mk), i+120)
        print(f"\n--- Makefile lines {start}-{end} (around update-manifests at {i}) ---")
        for j in range(start,end+1):
            print(f"{j:5d}\t{mk[j-1]}")
        break
PY

echo
echo "== Check how config/manifests or config/manager/config/default might be generated =="
rg -n "config/manager/config/default|config/manager/config/openshift-default|config/manifests|config/manager/manifests" Makefile config -S || true

echo
echo "== Check for scripts that create those directories =="
find hack scripts tools -type f -maxdepth 3 -print 2>/dev/null | head -n 50

Repository: medik8s/storage-based-remediation

Length of output: 9442


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== config/ tree (top level) =="
ls -la config | head -n 200

echo
echo "== config/default contents =="
ls -la config/default 2>/dev/null || true
if [ -f config/default/kustomization.yaml ]; then
  echo "--- config/default/kustomization.yaml (head) ---"
  cat -n config/default/kustomization.yaml | sed -n '1,200p'
fi

echo
echo "== config/openshift-default contents =="
ls -la config/openshift-default 2>/dev/null || true
if [ -f config/openshift-default/kustomization.yaml ]; then
  echo "--- config/openshift-default/kustomization.yaml (head) ---"
  cat -n config/openshift-default/kustomization.yaml | sed -n '1,200p'
fi

echo
echo "== config/manifests contents =="
ls -la config/manifests 2>/dev/null || true
if [ -f config/manifests/kustomization.yaml ]; then
  echo "--- config/manifests/kustomization.yaml (head) ---"
  cat -n config/manifests/kustomization.yaml | sed -n '1,200p'
fi

echo
echo "== Search for RELATED_IMAGE_AGENT / AGENT_IMG within config/ =="
rg -n "RELATED_IMAGE_AGENT|\$\{AGENT_IMG\}|AGENT_IMG" config -S || true

echo
echo "== Search for envsubst within Makefile and scripts =="
rg -n "envsubst" Makefile scripts tools config -S || true

Repository: medik8s/storage-based-remediation

Length of output: 13864


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Makefile targets: manifests, kustomize, generate kustomize =="
rg -n "^manifests:|^kustomize:|^generate:|generate kustomize|kustomize ##|kustomize " Makefile

echo
echo "== Search for envsubst usage across repo (AGENT_IMG-related) =="
rg -n "envsubst" -S . || true
rg -n "envsubst.*AGENT_IMG|AGENT_IMG.*envsubst|\\$\\{AGENT_IMG\\}" -S . || true

echo
echo "== Search for RELATED_IMAGE_AGENT across repo =="
rg -n "RELATED_IMAGE_AGENT" -S . || true

echo
echo "== Search for substitutions of AGENT_IMG besides bundle envsubst =="
rg -n "AGENT_IMG" -S Makefile scripts hack config test || true

echo
echo "== Where config/manager/config/* is created (if at all) =="
rg -n "config/manager/config" -S Makefile hack scripts tools || true

echo
echo "== Where config/default/config/manifests are referenced relative to config/manager =="
rg -n "cd config/manager.*build config/(default|openshift-default|manifests)" -S Makefile

Repository: medik8s/storage-based-remediation

Length of output: 6573


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Makefile: manifests target =="
rg -n "^manifests:" Makefile
python3 - <<'PY'
import pathlib, re
mk = pathlib.Path("Makefile").read_text().splitlines()
for i,l in enumerate(mk, start=1):
    if re.match(r'^manifests:', l):
        start=max(1,i-20); end=min(len(mk), i+120)
        print(f"--- Makefile lines {start}-{end} (around manifests at {i}) ---")
        for j in range(start,end+1):
            print(f"{j:5d}\t{mk[j-1]}")
        break
PY

echo
echo "== Makefile: kustomize target =="
rg -n "^kustomize:" Makefile
python3 - <<'PY'
import pathlib, re
mk = pathlib.Path("Makefile").read_text().splitlines()
for i,l in enumerate(mk, start=1):
    if re.match(r'^kustomize:', l):
        start=max(1,i-20); end=min(len(mk), i+120)
        print(f"--- Makefile lines {start}-{end} (around kustomize at {i}) ---")
        for j in range(start,end+1):
            print(f"{j:5d}\t{mk[j-1]}")
        break
PY

echo
echo "== Makefile: check for any generation of config/manager/config/* =="
rg -n "config/manager/config/default|config/manager/config/openshift-default|config/manager/config/manifests|config/manager/config" Makefile || true
rg -n "config/default|config/openshift-default|config/manifests" Makefile | head -n 80 || true

echo
echo "== Show Makefile dependency graph around build-installer/deploy =="
python3 - <<'PY'
import pathlib, re
mk = pathlib.Path("Makefile").read_text().splitlines()
targets = ["build-installer","build-openshift-installer","deploy","kustomize","manifests","generate"]
for t in targets:
    for i,l in enumerate(mk, start=1):
        if re.match(r'^'+re.escape(t)+r':', l):
            print("\nTarget:", t, "line", i)
            print(l)
            # print next 10 lines
            for j in range(i, min(i+15, len(mk))+1):
                pass
            break
PY

Repository: medik8s/storage-based-remediation

Length of output: 15240


Fix ${AGENT_IMG} placeholder substitution for non-bundle renders

config/manager/kustomization.yaml patches the controller-manager Deployment to set RELATED_IMAGE_AGENT to value: ${AGENT_IMG}. The OLM bundle path substitutes it via envsubst '$$AGENT_IMG', but make build-installer, make build-openshift-installer, and make deploy render config/default / config/openshift-default without envsubst, so ${AGENT_IMG} can reach the deployed controller literally.

💡 Suggested fix (Makefile) to apply envsubst to all render paths
 .PHONY: build-installer
 build-installer: update-manifests manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment.
 	mkdir -p dist
 	cd config/manager && $(KUSTOMIZE) edit set image controller=$(QUAY_OPERATOR_NAME):$(IMAGE_TAG)
-	$(KUSTOMIZE) build config/default > dist/install.yaml
+	$(KUSTOMIZE) build config/default | envsubst '$$AGENT_IMG' > dist/install.yaml

 .PHONY: build-openshift-installer
 build-openshift-installer: update-manifests manifests generate kustomize ## Generate a consolidated YAML with CRDs, deployment, and OpenShift SecurityContextConstraints.
 	mkdir -p dist
 	cd config/manager && $(KUSTOMIZE) edit set image controller=$(QUAY_OPERATOR_NAME):$(IMAGE_TAG)
-	$(KUSTOMIZE) build config/openshift-default > dist/install.yaml
+	$(KUSTOMIZE) build config/openshift-default | envsubst '$$AGENT_IMG' > dist/install.yaml

 .PHONY: deploy
 deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
 	cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
-	$(KUSTOMIZE) build config/default | $(KUBECTL) apply -f -
+	$(KUSTOMIZE) build config/default | envsubst '$$AGENT_IMG' | $(KUBECTL) apply -f -
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@config/manager/kustomization.yaml` around lines 9 - 18, The patch in
config/manager/kustomization.yaml inserts RELATED_IMAGE_AGENT with a literal
value of ${AGENT_IMG}, which is only being envsubst'ed for the OLM bundle;
update the build/render pipeline so non-bundle paths also perform envsubst.
Specifically, modify the Makefile targets (build-installer,
build-openshift-installer, deploy) or their underlying render steps that process
config/default and config/openshift-default to run envsubst on the rendered YAML
(or otherwise substitute AGENT_IMG) so RELATED_IMAGE_AGENT in the
controller-manager Deployment is replaced with the actual AGENT_IMG value rather
than left as the literal ${AGENT_IMG}.

2 changes: 1 addition & 1 deletion config/manager/manager.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,4 @@ spec:
app.kubernetes.io/name: sbr-operator
maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: DoNotSchedule
whenUnsatisfiable: DoNotSchedule
64 changes: 13 additions & 51 deletions internal/controller/storagebasedremediationconfig_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,52 +182,16 @@ func (r *StorageBasedRemediationConfigReconciler) emitEventf(
}
}

// getOperatorImage discovers the operator's own image by querying the current pod
// It uses environment variables (POD_NAME, POD_NAMESPACE) to find the current pod
// and extracts the image from the pod spec
func (r *StorageBasedRemediationConfigReconciler) getOperatorImage(ctx context.Context, logger logr.Logger) string {
// In CI, RELATED_IMAGE_SBR_AGENT is injected via `oc set env` after bundle installation,
// pointing to the CI-built agent image. In non-CI runs this env var is not set,
// so we fall through to pod image discovery and derivation.
if img := os.Getenv("RELATED_IMAGE_SBR_AGENT"); img != "" {
logger.Info("Using RELATED_IMAGE_SBR_AGENT for agent image", "image", img)
return img
}

// Try to get pod information from environment variables (set by Downward API)
podName := os.Getenv("POD_NAME")
podNamespace := os.Getenv("POD_NAMESPACE")

if podName == "" || podNamespace == "" {
logger.Error(nil, "POD_NAME or POD_NAMESPACE environment variables not set, using fallback")
return DefaultSBRAgentImage
}

// Get the current pod
var pod corev1.Pod
err := r.Get(ctx, types.NamespacedName{Name: podName, Namespace: podNamespace}, &pod)
if err != nil {
logger.Error(err, "Failed to get operator pod", "podName", podName, "podNamespace", podNamespace)
return DefaultSBRAgentImage // Fallback to default
}

// Find the manager container (operator container)
for _, container := range pod.Spec.Containers {
if container.Name == "manager" {
logger.Info("Found operator image", "image", container.Image)
return container.Image
}
}

// If manager container not found, use the first container's image
if len(pod.Spec.Containers) > 0 {
image := pod.Spec.Containers[0].Image
logger.Error(nil, "Using first container image as operator image", "image", image)
return image
}

logger.Error(nil, "No containers found in operator pod, using fallback")
return DefaultSBRAgentImage
// getAgentImage reads the agent image from the RELATED_IMAGE_AGENT environment variable.
// This variable is set during deployment via the operator-sdk bundle generation and
// kustomize configuration. It must always be set for the operator to function correctly.
func (r *StorageBasedRemediationConfigReconciler) getAgentImage(logger logr.Logger) (string, error) {
img := os.Getenv(medik8sv1alpha1.RelatedImageAgent)
if img == "" {
return "", fmt.Errorf("%s environment variable not set", medik8sv1alpha1.RelatedImageAgent)
}
logger.Info("Using agent image from environment", "env var", medik8sv1alpha1.RelatedImageAgent, "image", img)
return img, nil
}

// isRunningOnOpenShift detects if the operator is running on OpenShift
Expand Down Expand Up @@ -1020,14 +984,12 @@ func (r *StorageBasedRemediationConfigReconciler) Reconcile(ctx context.Context,
return ctrl.Result{Requeue: true}, r.Update(ctx, &sbrConfig)
}

// Get the operator image first for logging and DaemonSet creation
operatorImage := r.getOperatorImage(ctx, logger)

agentImage, err := medik8sv1alpha1.DeriveAgentImageFromOperator(operatorImage)
// Get the agent image from environment variable
agentImage, err := r.getAgentImage(logger)
if err != nil {
logger.Error(err, "Failed to get agent image")
return ctrl.Result{RequeueAfter: InitialStorageBasedRemediationConfigRetryDelay}, err
}
logger.Info("Resolved agent image", "agentImage", agentImage)
logger.V(1).Info("Starting StorageBasedRemediationConfig reconciliation",
"spec.image", agentImage,
"namespace", sbrConfig.Namespace,
Expand Down
Loading
Loading