Skip to content

Commit 5a80fab

Browse files
committed
ci: address review feedback on OCI immutability guard
1 parent fc4a00a commit 5a80fab

3 files changed

Lines changed: 58 additions & 3 deletions

File tree

scripts/deploy-camunda/cmd/matrix.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ This command calls deploy.Execute() for each matrix entry.`,
602602
f.BoolVar(&ensureDockerHub, "ensure-docker-hub", false, "Ensure Docker Hub registry pull secret is created in each entry's namespace")
603603
f.BoolVar(&useLatest, "use-latest", false, "Use values-latest.yaml from each chart root instead of values-digest.yaml")
604604
f.BoolVar(&useQA, "use-qa", false, "Force the base-qa layer to be included for all entries, regardless of per-scenario qa config")
605-
f.BoolVar(&forceImageOverrides, "force-image-overrides", false, "Bypass OCI immutability guard: allow image version overrides even when --chart-ref is set")
605+
f.BoolVar(&forceImageOverrides, "force-image-overrides", false, "Bypass OCI immutability guard: allow Go-layer image overlays (base-image-tags, chart-root overlays) even when --chart-ref is set. Note: env-file IMAGE_TAG keys stripped at the workflow layer are not restored by this flag.")
606606
f.BoolVarP(&yes, "yes", "y", false, "Skip confirmation prompts (e.g., e2e threshold warning)")
607607
f.StringVar(&logDir, "log-dir", "", "Write logs to this directory and show a live status table (auto-generated when running in a TTY)")
608608
f.StringArrayVar(&extraHelmArgs, "extra-helm-arg", nil, "Extra argument appended to every helm command (repeatable, e.g. --extra-helm-arg=--set-file=global.license.secret.inlineSecret=/tmp/license.txt)")

scripts/deploy-camunda/matrix/matrix_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,54 @@ func TestSanitizeEnvFileForOCIImmutability(t *testing.T) {
11931193

11941194
// --- resolveUseVaultBackedSecrets tests ---
11951195

1196+
func TestOCIImmutabilityForceOverridesRestoresDigest(t *testing.T) {
1197+
chartPath := t.TempDir()
1198+
for _, name := range []string{"values-digest.yaml", "values-enterprise.yaml", "values-latest.yaml"} {
1199+
if err := os.WriteFile(filepath.Join(chartPath, name), []byte("# test\n"), 0o644); err != nil {
1200+
t.Fatalf("write %s: %v", name, err)
1201+
}
1202+
}
1203+
1204+
// ForceImageOverrides: true + ImageTags: false -> digest overlay should be restored
1205+
entry := Entry{Enterprise: true, ImageTags: false}
1206+
opts := RunOptions{ChartRef: "oci://registry.camunda.cloud/team-distribution/camunda-platform", ForceImageOverrides: true}
1207+
got := resolveChartRootOverlaysQuiet(chartPath, entry, opts)
1208+
expected := "enterprise,digest"
1209+
if strings.Join(got, ",") != expected {
1210+
t.Fatalf("resolveChartRootOverlaysQuiet() = %v, want [enterprise, digest] when force-overrides restores non-image-tags path", got)
1211+
}
1212+
}
1213+
1214+
func TestSanitizeEnvFileAllImageTags(t *testing.T) {
1215+
// All keys are IMAGE_TAG -> result should be empty path (no temp file needed)
1216+
envFile := filepath.Join(t.TempDir(), "values.env")
1217+
content := "E2E_TESTS_OPTIMIZE_IMAGE_TAG=8.8-SNAPSHOT\nE2E_TESTS_OPERATE_IMAGE_TAG=8.8-SNAPSHOT\n"
1218+
if err := os.WriteFile(envFile, []byte(content), 0o644); err != nil {
1219+
t.Fatalf("write env file: %v", err)
1220+
}
1221+
1222+
got, cleanup, err := sanitizeEnvFileForOCIImmutability(envFile, RunOptions{ChartRef: "oci://registry.camunda.cloud/team-distribution/camunda-platform"})
1223+
if err != nil {
1224+
t.Fatalf("sanitizeEnvFileForOCIImmutability() error = %v", err)
1225+
}
1226+
defer cleanup()
1227+
if got != "" {
1228+
t.Fatalf("sanitizeEnvFileForOCIImmutability() = %q, want empty path when all keys are image tags", got)
1229+
}
1230+
}
1231+
1232+
func TestSanitizeEnvFileNoEnvFile(t *testing.T) {
1233+
// OCI mode with no env file -> should short-circuit cleanly
1234+
got, cleanup, err := sanitizeEnvFileForOCIImmutability("", RunOptions{ChartRef: "oci://registry.camunda.cloud/team-distribution/camunda-platform"})
1235+
if err != nil {
1236+
t.Fatalf("sanitizeEnvFileForOCIImmutability() error = %v", err)
1237+
}
1238+
defer cleanup()
1239+
if got != "" {
1240+
t.Fatalf("sanitizeEnvFileForOCIImmutability() = %q, want empty string for no env file", got)
1241+
}
1242+
}
1243+
11961244
func TestResolveUseVaultBackedSecrets(t *testing.T) {
11971245
tests := []struct {
11981246
name string

scripts/deploy-camunda/matrix/runner.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ func effectiveImageTags(entry Entry, opts RunOptions) bool {
232232

233233
func resolveChartRootOverlays(entry Entry, opts RunOptions) []string {
234234
if ociImmutabilityMode(opts) {
235+
// OCI artifacts bake all image versions; skip overlays that would
236+
// override them (includes enterprise sub-chart image pins).
235237
return nil
236238
}
237239

@@ -600,7 +602,7 @@ func resolveStep1ValuesFromQuiet(entry Entry) string {
600602

601603
// resolveChartRootOverlaysQuiet returns the list of chart-root overlays that exist on disk.
602604
// This is a dry-run helper — best-effort, silently filters to existing files only.
603-
// enterprise is composable (changes registry/repo, not tags).
605+
// enterprise adds registry/repo config and sub-chart image pins (Keycloak, PostgreSQL).
604606
// digest, latest, and image-tags are mutually exclusive for image version resolution:
605607
// - image-tags (SNAPSHOT tags from env) takes priority over digest/latest
606608
// - useLatest selects values-latest.yaml instead of values-digest.yaml
@@ -610,6 +612,11 @@ func resolveChartRootOverlaysQuiet(chartPath string, entry Entry, opts RunOption
610612
if chartPath == "" {
611613
return nil
612614
}
615+
if ociImmutabilityMode(opts) {
616+
logging.Logger.Warn().
617+
Str("chartRef", opts.ChartRef).
618+
Msg("OCI immutability mode: skipping chart-root image overlays (dry-run)")
619+
}
613620
overlays := resolveChartRootOverlays(entry, opts)
614621
// Filter to only overlays whose files exist on disk.
615622
var existing []string
@@ -1578,10 +1585,10 @@ func executeEntry(ctx context.Context, entry Entry, opts RunOptions, entryIndex
15781585
kubeCtx := resolveKubeContext(opts, platform)
15791586
envFile := resolveEnvFile(opts, entry.Version)
15801587
envFile, cleanupEnvFile, err := sanitizeEnvFileForOCIImmutability(envFile, opts)
1588+
defer cleanupEnvFile() // safe: cleanup is always a valid no-op func even on error
15811589
if err != nil {
15821590
return RunResult{Entry: entry, Namespace: namespace, KubeContext: kubeCtx, Error: err}
15831591
}
1584-
defer cleanupEnvFile()
15851592
useVault := resolveUseVaultBackedSecrets(opts, platform)
15861593

15871594
// Compute the scenario directory. deploy.Execute uses this to resolve

0 commit comments

Comments
 (0)