Skip to content
Closed
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
23 changes: 17 additions & 6 deletions .github/workflows/test-integration-runner.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -617,11 +617,17 @@ jobs:
CHART_REF_ARGS+=(--chart-version "${HELM_CHART_VERSION}")
fi

# Convert VALUES_CONFIG JSON to .env file for image tag substitution.
# The .env file is loaded by buildScenarioEnv() and provides values for
# $E2E_TESTS_*_IMAGE_TAG placeholders in base-image-tags.yaml.
# Image version source selection:
# TEST_IMAGE_TAGS=false → --disable-image-tags; when OCI chart is set the runner
# applies no version overlay (chart's values.yaml is authoritative); for local
# chart deploys the runner falls back to values-digest.yaml.
# TEST_IMAGE_TAGS=true → load VALUES_CONFIG as .env file so base-image-tags.yaml
# substitution resolves $E2E_TESTS_*_IMAGE_TAG placeholders (SNAPSHOT builds).
IMAGE_TAGS_ARGS=()
ENV_FILE_ARGS=()
if [[ -n "${VALUES_CONFIG:-}" && "${VALUES_CONFIG}" != "{}" ]]; then
if [[ "${TEST_IMAGE_TAGS:-true}" == "false" ]]; then
IMAGE_TAGS_ARGS=(--disable-image-tags)
elif [[ -n "${VALUES_CONFIG:-}" && "${VALUES_CONFIG}" != "{}" ]]; then
echo "${VALUES_CONFIG}" | jq -r 'to_entries[] | "\(.key)=\(.value)"' > /tmp/values-config.env
ENV_FILE_ARGS=(--env-file /tmp/values-config.env)
fi
Expand All @@ -640,6 +646,7 @@ jobs:
--skip-dependency-update=false \
--timeout 20 \
--extra-helm-set "global.host=${{ steps.setup.outputs.ingress-host }}" \
${IMAGE_TAGS_ARGS[@]+"${IMAGE_TAGS_ARGS[@]}"} \
${ENV_FILE_ARGS[@]+"${ENV_FILE_ARGS[@]}"} \
${CHART_REF_ARGS[@]+"${CHART_REF_ARGS[@]}"} \
${EXTRA_HELM_ARGS[@]+"${EXTRA_HELM_ARGS[@]}"} \
Expand Down Expand Up @@ -919,9 +926,12 @@ jobs:
CHART_REF_ARGS+=(--chart-version "${HELM_CHART_VERSION}")
fi

# Convert VALUES_CONFIG JSON to .env file for image tag substitution.
# Image version source selection (same logic as install block above).
IMAGE_TAGS_ARGS=()
ENV_FILE_ARGS=()
if [[ -n "${VALUES_CONFIG:-}" && "${VALUES_CONFIG}" != "{}" ]]; then
if [[ "${TEST_IMAGE_TAGS:-true}" == "false" ]]; then
IMAGE_TAGS_ARGS=(--disable-image-tags)
elif [[ -n "${VALUES_CONFIG:-}" && "${VALUES_CONFIG}" != "{}" ]]; then
echo "${VALUES_CONFIG}" | jq -r 'to_entries[] | "\(.key)=\(.value)"' > /tmp/values-config.env
ENV_FILE_ARGS=(--env-file /tmp/values-config.env)
fi
Expand All @@ -940,6 +950,7 @@ jobs:
--skip-dependency-update=false \
--timeout 20 \
--extra-helm-set "global.host=${{ steps.setup.outputs.ingress-host }}" \
${IMAGE_TAGS_ARGS[@]+"${IMAGE_TAGS_ARGS[@]}"} \
${ENV_FILE_ARGS[@]+"${ENV_FILE_ARGS[@]}"} \
${CHART_REF_ARGS[@]+"${CHART_REF_ARGS[@]}"} \
${EXTRA_HELM_ARGS[@]+"${EXTRA_HELM_ARGS[@]}"} \
Expand Down
3 changes: 3 additions & 0 deletions scripts/deploy-camunda/cmd/matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ func newMatrixRunCommand() *cobra.Command {
dockerHubPassword string
ensureDockerHub bool
useLatest bool
disableImageTags bool
useQA bool
yes bool
logDir string
Expand Down Expand Up @@ -498,6 +499,7 @@ This command calls deploy.Execute() for each matrix entry.`,
DockerHubPassword: dockerHubPassword,
EnsureDockerHub: ensureDockerHub,
UseLatest: useLatest,
DisableImageTags: disableImageTags,
UseQA: useQA,
ExtraHelmArgs: extraHelmArgs,
ExtraHelmSets: extraHelmSets,
Expand Down Expand Up @@ -599,6 +601,7 @@ This command calls deploy.Execute() for each matrix entry.`,
f.StringVar(&dockerHubPassword, "dockerhub-password", "", "Docker Hub registry password (defaults to DOCKERHUB_PASSWORD or TEST_DOCKER_PASSWORD env var)")
f.BoolVar(&ensureDockerHub, "ensure-docker-hub", false, "Ensure Docker Hub registry pull secret is created in each entry's namespace")
f.BoolVar(&useLatest, "use-latest", false, "Use values-latest.yaml from each chart root instead of values-digest.yaml")
f.BoolVar(&disableImageTags, "disable-image-tags", false, "Disable image tag overrides from env vars for all entries, regardless of per-scenario config. Use when deploying an OCI release artifact whose values.yaml image versions are authoritative.")
f.BoolVar(&useQA, "use-qa", false, "Force the base-qa layer to be included for all entries, regardless of per-scenario qa config")
f.BoolVarP(&yes, "yes", "y", false, "Skip confirmation prompts (e.g., e2e threshold warning)")
f.StringVar(&logDir, "log-dir", "", "Write logs to this directory and show a live status table (auto-generated when running in a TTY)")
Expand Down
117 changes: 117 additions & 0 deletions scripts/deploy-camunda/matrix/matrix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1922,3 +1922,120 @@ func TestChartRefOverride_UpgradeStep1Unaffected(t *testing.T) {
t.Errorf("Step 2 ChartVersion = %q, want %q", step2Flags.ChartVersion, opts.ChartRefVersion)
}
}

// --- effectiveImageTags tests ---

func TestEffectiveImageTags(t *testing.T) {
tests := []struct {
name string
entryImageTags bool
opts RunOptions
want bool
}{
{
name: "scenario flag true, no runtime override",
entryImageTags: true,
opts: RunOptions{},
want: true,
},
{
name: "scenario flag true, DisableImageTags overrides to false",
entryImageTags: true,
opts: RunOptions{DisableImageTags: true},
want: false,
},
{
name: "scenario flag false, no override",
entryImageTags: false,
opts: RunOptions{},
want: false,
},
{
name: "scenario flag false, DisableImageTags is a no-op",
entryImageTags: false,
opts: RunOptions{DisableImageTags: true},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := effectiveImageTags(tt.entryImageTags, tt.opts)
if got != tt.want {
t.Errorf("effectiveImageTags(%v, opts) = %v, want %v", tt.entryImageTags, got, tt.want)
}
})
}
}

// --- chartRootOverlays tests ---

func TestChartRootOverlays(t *testing.T) {
tests := []struct {
name string
entry Entry
opts RunOptions
want []string
}{
{
name: "image-tags true (scenario default): no version overlay; env-file handles SNAPSHOT injection",
entry: Entry{ImageTags: true},
opts: RunOptions{},
want: nil,
},
{
name: "DisableImageTags + local chart: falls back to digest",
entry: Entry{ImageTags: true},
opts: RunOptions{DisableImageTags: true},
want: []string{"digest"},
},
{
name: "DisableImageTags + OCI: no overlay at all",
entry: Entry{ImageTags: true},
opts: RunOptions{DisableImageTags: true, ChartRef: "oci://registry.camunda.cloud/team-distribution/camunda-platform"},
want: nil,
},
{
name: "DisableImageTags + UseLatest + local chart: values-latest.yaml",
entry: Entry{ImageTags: true},
opts: RunOptions{DisableImageTags: true, UseLatest: true},
want: []string{"latest"},
},
{
name: "OCI always wins over UseLatest",
entry: Entry{ImageTags: true},
opts: RunOptions{DisableImageTags: true, UseLatest: true, ChartRef: "oci://registry.camunda.cloud/team-distribution/camunda-platform"},
want: nil,
},
{
name: "enterprise composes with digest",
entry: Entry{Enterprise: true, ImageTags: true},
opts: RunOptions{DisableImageTags: true},
want: []string{"enterprise", "digest"},
},
{
name: "enterprise + OCI: enterprise overlay kept, no version overlay",
entry: Entry{Enterprise: true, ImageTags: true},
opts: RunOptions{DisableImageTags: true, ChartRef: "oci://registry.camunda.cloud/team-distribution/camunda-platform"},
want: []string{"enterprise"},
},
{
name: "enterprise composes with latest",
entry: Entry{Enterprise: true, ImageTags: true},
opts: RunOptions{DisableImageTags: true, UseLatest: true},
want: []string{"enterprise", "latest"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := chartRootOverlays(tt.entry, tt.opts)
if len(got) != len(tt.want) {
t.Fatalf("chartRootOverlays() = %v, want %v", got, tt.want)
}
for i := range got {
if got[i] != tt.want[i] {
t.Errorf("chartRootOverlays()[%d] = %q, want %q", i, got[i], tt.want[i])
}
}
})
}
}
78 changes: 40 additions & 38 deletions scripts/deploy-camunda/matrix/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ type RunOptions struct {
// UseLatest applies values-latest.yaml from each chart root instead of values-digest.yaml.
// This overrides the default digest-based image pinning with the latest available tags.
UseLatest bool
// DisableImageTags disables SNAPSHOT image tag overrides from env vars for all entries,
// regardless of per-scenario image-tags config in ci-test-config.yaml.
// Runtime override that trumps entry.ImageTags — same pattern as UseQA.
// Use when deploying an OCI release artifact whose values.yaml image versions are authoritative.
DisableImageTags bool
// UseQA forces the base-qa layer to be included for all entries, regardless of each
// entry's per-scenario qa setting in ci-test-config.yaml.
UseQA bool
Expand Down Expand Up @@ -389,7 +394,7 @@ func dryRun(entries []Entry, opts RunOptions) []RunResult {
InfraType: entry.InfraType,
Flow: entry.Flow,
QA: entry.QA || opts.UseQA,
ImageTags: entry.ImageTags,
ImageTags: effectiveImageTags(entry.ImageTags, opts),
Upgrade: entry.Upgrade,
})
if buildErr != nil {
Expand Down Expand Up @@ -431,7 +436,7 @@ func dryRun(entries []Entry, opts RunOptions) []RunResult {
preUpgradeScript: resolvePreUpgradeScriptQuiet(opts.RepoRoot, entry),
upgradeOnly: versionmatrix.IsUpgradeOnlyFlow(entry.Flow),
step1ValuesFrom: resolveStep1ValuesFromQuiet(entry),
chartRootOverlays: resolveChartRootOverlaysQuiet(entry.ChartPath, entry, opts.UseLatest),
chartRootOverlays: resolveChartRootOverlaysQuiet(entry.ChartPath, entry, opts),
})
results = append(results, RunResult{Entry: entry, Namespace: namespace, KubeContext: kubeCtx})
}
Expand Down Expand Up @@ -508,31 +513,47 @@ func resolveStep1ValuesFromQuiet(entry Entry) string {
return prev
}

// resolveChartRootOverlaysQuiet returns the list of chart-root overlays that exist on disk.
// This is a dry-run helper — best-effort, silently filters to existing files only.
// enterprise is composable (changes registry/repo, not tags).
// digest, latest, and image-tags are mutually exclusive for image version resolution:
// - image-tags (SNAPSHOT tags from env) takes priority over digest/latest
// - useLatest selects values-latest.yaml instead of values-digest.yaml
// - digest is the CI default when neither image-tags nor useLatest is active
func resolveChartRootOverlaysQuiet(chartPath string, entry Entry, useLatest bool) []string {
if chartPath == "" {
return nil
// effectiveImageTags returns whether SNAPSHOT tag overrides from env vars should be applied.
// opts.DisableImageTags is a runtime override that trumps the per-scenario flag.
func effectiveImageTags(entryImageTags bool, opts RunOptions) bool {
if opts.DisableImageTags {
return false
}
return entryImageTags
}

// chartRootOverlays returns the chart-root overlay names for an entry.
// enterprise is composable with any image source.
// digest / latest / image-tags are mutually exclusive for image version resolution:
// - effectiveImageTags=true → SNAPSHOT path; caller supplies base-image-tags.yaml via --env-file
// - OCI (ChartRef set) → no overlay; chart's values.yaml is authoritative
// - UseLatest → values-latest.yaml (pinned release tags from local repo)
// - default → values-digest.yaml (SNAPSHOT pinned by sha256)
func chartRootOverlays(entry Entry, opts RunOptions) []string {
var overlays []string
if entry.Enterprise {
overlays = append(overlays, "enterprise")
}
if !entry.ImageTags {
if useLatest {
if !effectiveImageTags(entry.ImageTags, opts) {
if opts.ChartRef != "" {
// OCI artifact: the chart ships its own image versions; no overlay needed.
} else if opts.UseLatest {
overlays = append(overlays, "latest")
} else {
overlays = append(overlays, "digest")
}
}
// Filter to only overlays whose files exist on disk.
return overlays
}

// resolveChartRootOverlaysQuiet returns chart-root overlays that exist on disk.
// Dry-run helper: best-effort, silently filters to present files only.
func resolveChartRootOverlaysQuiet(chartPath string, entry Entry, opts RunOptions) []string {
if chartPath == "" {
return nil
}
var existing []string
for _, name := range overlays {
for _, name := range chartRootOverlays(entry, opts) {
path := filepath.Join(chartPath, "values-"+name+".yaml")
if _, err := os.Stat(path); err == nil {
existing = append(existing, name)
Expand Down Expand Up @@ -717,7 +738,7 @@ func coverageReport(entries []Entry, opts RunOptions) []RunResult {
InfraType: entry.InfraType,
Flow: entry.Flow,
QA: entry.QA || opts.UseQA,
ImageTags: entry.ImageTags,
ImageTags: effectiveImageTags(entry.ImageTags, opts),
Upgrade: entry.Upgrade,
})
if buildErr != nil {
Expand Down Expand Up @@ -1528,26 +1549,7 @@ func executeEntry(ctx context.Context, entry Entry, opts RunOptions, entryIndex
ChartPath: entry.ChartPath,
SkipDependencyUpdate: opts.SkipDependencyUpdate,
RepoRoot: opts.RepoRoot,
// Build chart-root overlays.
// enterprise is composable (changes registry/repo, not tags).
// digest, latest, and image-tags are mutually exclusive for image version resolution:
// - image-tags (SNAPSHOT tags from env) takes priority over digest/latest
// - useLatest selects values-latest.yaml instead of values-digest.yaml
// - digest is the CI default when neither image-tags nor useLatest is active
ChartRootOverlays: func() []string {
var overlays []string
if entry.Enterprise {
overlays = append(overlays, "enterprise")
}
if !entry.ImageTags {
if opts.UseLatest {
overlays = append(overlays, "latest")
} else {
overlays = append(overlays, "digest")
}
}
return overlays
}(),
ChartRootOverlays: chartRootOverlays(entry, opts),
},
Deployment: config.DeploymentFlags{
Namespace: flagsNamespace,
Expand Down Expand Up @@ -1622,7 +1624,7 @@ func executeEntry(ctx context.Context, entry Entry, opts RunOptions, entryIndex
Features: entry.Features,
InfraType: entry.InfraType,
QA: entry.QA || opts.UseQA,
ImageTags: entry.ImageTags,
ImageTags: effectiveImageTags(entry.ImageTags, opts),
UpgradeFlow: entry.Upgrade,
},
}
Expand Down
Loading