From 8390cece2fcae55891b465db572659906a9d72a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Allamand?= Date: Mon, 11 May 2026 13:28:11 +0200 Subject: [PATCH 1/2] feat: multi-repo overlay architecture for addon values (#574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for an optional overlay Git repository that provides value overrides for addons without modifying the source platform repo. Changes: - Terraform: 3 new variables (overlay_repo_url, revision, base_path) - Bootstrap addons.yaml: ref:values → ref:defaults + conditional ref:overlay - appset-chart: $values/ → $defaults/ + conditional $overlay/ paths - Fully backward-compatible: empty overlay_repo_url = no change in behavior --- gitops/bootstrap/addons.yaml | 35 +++++++++++++------ .../templates/_application_set.tpl | 21 +++++++---- .../templates/application-set.yaml | 10 +++++- platform-charts/appset-chart/values.yaml | 5 +++ platform/infra/terraform/common/locals.tf | 5 +++ platform/infra/terraform/common/variables.tf | 20 +++++++++++ 6 files changed, 77 insertions(+), 19 deletions(-) diff --git a/gitops/bootstrap/addons.yaml b/gitops/bootstrap/addons.yaml index b15224979..dbb19c27b 100644 --- a/gitops/bootstrap/addons.yaml +++ b/gitops/bootstrap/addons.yaml @@ -23,7 +23,12 @@ spec: sources: - repoURL: '{{ .metadata.annotations.addonsRepoURL }}' targetRevision: '{{ .metadata.annotations.addonsRepoRevision }}' - ref: values + ref: defaults + {{- if .metadata.annotations.overlay_repo_url }} + - repoURL: '{{ .metadata.annotations.overlay_repo_url }}' + targetRevision: '{{ .metadata.annotations.overlay_repo_revision }}' + ref: overlay + {{- end }} - repoURL: '{{ .metadata.annotations.addonsRepoURL }}' path: 'platform-charts/appset-chart' targetRevision: '{{ .metadata.annotations.addonsRepoRevision }}' @@ -32,16 +37,24 @@ spec: ignoreMissingValueFiles: true valueFiles: - values.yaml - - $values/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/_defaults.yaml - - $values/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/core.yaml - - $values/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/gitops.yaml - - $values/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/security.yaml - - $values/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/observability.yaml - - $values/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/platform.yaml - - $values/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/ml.yaml - - $values/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/agents.yaml - - $values/{{ .metadata.annotations.addonsRepoBasepath }}overlays/environments/{{ .metadata.labels.environment }}/overrides.yaml - - $values/{{ .metadata.annotations.addonsRepoBasepath }}overlays/clusters/{{ .nameNormalized }}/overrides.yaml + - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/_defaults.yaml + - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/core.yaml + - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/gitops.yaml + - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/security.yaml + - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/observability.yaml + - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/platform.yaml + - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/ml.yaml + - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/agents.yaml + - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}overlays/environments/{{ .metadata.labels.environment }}/overrides.yaml + - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}overlays/clusters/{{ .nameNormalized }}/overrides.yaml + {{- if .metadata.annotations.overlay_repo_url }} + - $overlay/{{ .metadata.annotations.overlay_repo_basepath }}overlays/environments/{{ .metadata.labels.environment }}/overrides.yaml + - $overlay/{{ .metadata.annotations.overlay_repo_basepath }}overlays/clusters/{{ .nameNormalized }}/overrides.yaml + {{- end }} + valuesObject: + overlayRepoURLGit: '{{ default "" .metadata.annotations.overlay_repo_url }}' + overlayRepoURLGitRevision: '{{ default "main" .metadata.annotations.overlay_repo_revision }}' + overlayRepoURLGitBasePath: '{{ default "" .metadata.annotations.overlay_repo_basepath }}' destination: namespace: argocd name: '{{ .name }}' diff --git a/platform-charts/appset-chart/templates/_application_set.tpl b/platform-charts/appset-chart/templates/_application_set.tpl index 6190c8d75..590caf03f 100644 --- a/platform-charts/appset-chart/templates/_application_set.tpl +++ b/platform-charts/appset-chart/templates/_application_set.tpl @@ -54,6 +54,7 @@ Layering order: 2. Per-addon custom valueFiles (from chartConfig.valueFiles) 3. Environment overlay values (from overlayBasePath/environments///values.yaml) 4. Cluster overlay values (from overlayBasePath/clusters///values.yaml) + 5. External overlay repo values (from overlayRepoURLGitBasePath, if configured) All paths use ignoreMissingValueFiles: true so missing files are silently skipped. */}} {{- define "application-sets.valueFiles" -}} @@ -65,22 +66,28 @@ All paths use ignoreMissingValueFiles: true so missing files are silently skippe {{/* 1. Default addon config values */}} {{- with .valueFiles }} {{- range . }} -- $values/{{ $values.repoURLGitBasePath }}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/{{ if $chartConfig.valuesFileName }}{{ $chartConfig.valuesFileName }}{{ else }}{{ . }}{{ end }} +- $defaults/{{ $values.repoURLGitBasePath }}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/{{ if $chartConfig.valuesFileName }}{{ $chartConfig.valuesFileName }}{{ else }}{{ . }}{{ end }} {{- if $values.useValuesFilePrefix }} -- $values/{{ $values.repoURLGitBasePath }}/{{ $values.valuesFilePrefix }}{{ . }}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/{{ if $chartConfig.valuesFileName }}{{ $chartConfig.valuesFileName }}{{ else }}values.yaml{{ end }} +- $defaults/{{ $values.repoURLGitBasePath }}/{{ $values.valuesFilePrefix }}{{ . }}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/{{ if $chartConfig.valuesFileName }}{{ $chartConfig.valuesFileName }}{{ else }}values.yaml{{ end }} {{- end }} {{- end }} {{- end }} {{/* 2. Per-addon custom valueFiles */}} {{- with $chartConfig.valueFiles }} {{- range . }} -- $values/{{ $values.repoURLGitBasePath }}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/{{ if $chartConfig.valuesFileName }}{{ $chartConfig.valuesFileName }}{{ else }}{{ . }}{{ end }} +- $defaults/{{ $values.repoURLGitBasePath }}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/{{ if $chartConfig.valuesFileName }}{{ $chartConfig.valuesFileName }}{{ else }}{{ . }}{{ end }} {{- end }} {{- end }} -{{/* 3. Environment overlay values */}} +{{/* 3. Environment overlay values (same repo) */}} {{- if $values.overlayBasePath }} -- $values/{{ $values.overlayBasePath }}/environments/{{`{{.metadata.labels.environment}}`}}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/values.yaml -{{/* 4. Cluster overlay values */}} -- $values/{{ $values.overlayBasePath }}/clusters/{{`{{.nameNormalized}}`}}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/values.yaml +- $defaults/{{ $values.overlayBasePath }}/environments/{{`{{.metadata.labels.environment}}`}}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/values.yaml +{{/* 4. Cluster overlay values (same repo) */}} +- $defaults/{{ $values.overlayBasePath }}/clusters/{{`{{.nameNormalized}}`}}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/values.yaml +{{- end }} +{{/* 5. External overlay repo values */}} +{{- if $values.overlayRepoURLGit }} +- $overlay/{{ $values.overlayRepoURLGitBasePath }}configs/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/values.yaml +- $overlay/{{ $values.overlayRepoURLGitBasePath }}overlays/environments/{{`{{.metadata.labels.environment}}`}}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/values.yaml +- $overlay/{{ $values.overlayRepoURLGitBasePath }}overlays/clusters/{{`{{.nameNormalized}}`}}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/values.yaml {{- end }} {{- end }} diff --git a/platform-charts/appset-chart/templates/application-set.yaml b/platform-charts/appset-chart/templates/application-set.yaml index 80c485200..983b0fff3 100644 --- a/platform-charts/appset-chart/templates/application-set.yaml +++ b/platform-charts/appset-chart/templates/application-set.yaml @@ -7,6 +7,9 @@ {{- $repoURLGit := .Values.repoURLGit -}} {{- $repoURLGitRevision := .Values.repoURLGitRevision -}} {{- $repoURLGitBasePath := .Values.repoURLGitBasePath -}} +{{- $overlayRepoURLGit := .Values.overlayRepoURLGit -}} +{{- $overlayRepoURLGitRevision := .Values.overlayRepoURLGitRevision -}} +{{- $overlayRepoURLGitBasePath := .Values.overlayRepoURLGitBasePath -}} {{- $valueFiles := .Values.valueFiles -}} {{- $valuesFilePrefix := .Values.valuesFilePrefix -}} {{- $useValuesFilePrefix := (default false .Values.useValuesFilePrefix ) -}} @@ -129,7 +132,12 @@ spec: sources: - repoURL: {{ $repoURLGit | squote}} targetRevision: {{ $repoURLGitRevision | squote }} - ref: values + ref: defaults + {{- if $overlayRepoURLGit }} + - repoURL: {{ $overlayRepoURLGit | squote }} + targetRevision: {{ $overlayRepoURLGitRevision | squote }} + ref: overlay + {{- end }} {{- if eq (toString $chartConfig.enableAckPodIdentity) "true" }} {{ include "application-sets.pod-identity" (dict "chartName" ($chartConfig.chartName | default $nameNormalize) diff --git a/platform-charts/appset-chart/values.yaml b/platform-charts/appset-chart/values.yaml index 185c84287..2ae6d2f7c 100644 --- a/platform-charts/appset-chart/values.yaml +++ b/platform-charts/appset-chart/values.yaml @@ -29,3 +29,8 @@ repoURLGit: "{{.metadata.annotations.addonsRepoURL}}" repoURLGitRevision: "{{.metadata.annotations.addonsRepoRevision}}" repoURLGitBasePath: "{{.metadata.annotations.addonsRepoBasepath}}" overlayBasePath: "" + +# Overlay multi-repo (optional, injected by bootstrap/addons.yaml valuesObject) +overlayRepoURLGit: "" +overlayRepoURLGitRevision: "main" +overlayRepoURLGitBasePath: "" diff --git a/platform/infra/terraform/common/locals.tf b/platform/infra/terraform/common/locals.tf index 4c91acc3c..456b57fed 100644 --- a/platform/infra/terraform/common/locals.tf +++ b/platform/infra/terraform/common/locals.tf @@ -146,6 +146,11 @@ locals { addons_repo_basepath = var.gitops_addons_repo_base_path addons_repo_revision = var.gitops_addons_repo_revision }, + { + overlay_repo_url = var.overlay_repo_url + overlay_repo_revision = var.overlay_repo_revision + overlay_repo_basepath = var.overlay_repo_base_path + }, { workload_repo_url = local.gitops_workload_repo_url workload_repo_path = var.gitops_workload_repo_path diff --git a/platform/infra/terraform/common/variables.tf b/platform/infra/terraform/common/variables.tf index 2a769c5fd..6652da3b5 100644 --- a/platform/infra/terraform/common/variables.tf +++ b/platform/infra/terraform/common/variables.tf @@ -133,6 +133,26 @@ variable "gitops_addons_repo_revision" { description = "The name of branch or tag" default = "main" } + +# Overlay (optional multi-repo values override) +variable "overlay_repo_url" { + description = "Git URL for overlay values repo (empty = no overlay)" + type = string + default = "" +} + +variable "overlay_repo_revision" { + description = "Branch or tag for overlay repo" + type = string + default = "main" +} + +variable "overlay_repo_base_path" { + description = "Base path within overlay repo for addon values" + type = string + default = "gitops/addons/" +} + # Fleet variable "gitops_fleet_repo_name" { description = "The name of Git repo" From 79daa332d99de2b6684fdbfa76fe5b622e8eb0d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Allamand?= Date: Mon, 11 May 2026 19:23:26 +0200 Subject: [PATCH 2/2] fix: remove Go template conditionals from addons.yaml The bootstrap Application loads gitops/bootstrap/ as a directory source, which parses YAML before ArgoCD processes Go templates. The {{- if }} conditionals broke YAML parsing. Fix: - Always include overlay source, using 'or' to fall back to the main repo when overlay_repo_url is empty - Add empty overlay annotations to seed secret so missingkey=error doesn't fail on missing keys - ignoreMissingValueFiles: true handles non-existent overlay files --- .../kind-crossplane/Taskfile.yaml | 3 +++ gitops/bootstrap/addons.yaml | 18 +++++++----------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/cluster-providers/kind-crossplane/Taskfile.yaml b/cluster-providers/kind-crossplane/Taskfile.yaml index 2ee21baf0..13fc8b3f1 100644 --- a/cluster-providers/kind-crossplane/Taskfile.yaml +++ b/cluster-providers/kind-crossplane/Taskfile.yaml @@ -380,6 +380,9 @@ tasks: fleetRepoURL: "REPO_URL" fleetRepoRevision: "REPO_REVISION" fleetRepoBasepath: "REPO_BASEPATH" + overlay_repo_url: "" + overlay_repo_revision: "" + overlay_repo_basepath: "" aws_cluster_name: {{.HUB_CLUSTER_NAME}} labels: argocd.argoproj.io/secret-type: cluster diff --git a/gitops/bootstrap/addons.yaml b/gitops/bootstrap/addons.yaml index dbb19c27b..87b7d7377 100644 --- a/gitops/bootstrap/addons.yaml +++ b/gitops/bootstrap/addons.yaml @@ -24,11 +24,9 @@ spec: - repoURL: '{{ .metadata.annotations.addonsRepoURL }}' targetRevision: '{{ .metadata.annotations.addonsRepoRevision }}' ref: defaults - {{- if .metadata.annotations.overlay_repo_url }} - - repoURL: '{{ .metadata.annotations.overlay_repo_url }}' - targetRevision: '{{ .metadata.annotations.overlay_repo_revision }}' + - repoURL: '{{ or .metadata.annotations.overlay_repo_url .metadata.annotations.addonsRepoURL }}' + targetRevision: '{{ or .metadata.annotations.overlay_repo_revision .metadata.annotations.addonsRepoRevision }}' ref: overlay - {{- end }} - repoURL: '{{ .metadata.annotations.addonsRepoURL }}' path: 'platform-charts/appset-chart' targetRevision: '{{ .metadata.annotations.addonsRepoRevision }}' @@ -47,14 +45,12 @@ spec: - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}addons/registry/agents.yaml - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}overlays/environments/{{ .metadata.labels.environment }}/overrides.yaml - $defaults/{{ .metadata.annotations.addonsRepoBasepath }}overlays/clusters/{{ .nameNormalized }}/overrides.yaml - {{- if .metadata.annotations.overlay_repo_url }} - - $overlay/{{ .metadata.annotations.overlay_repo_basepath }}overlays/environments/{{ .metadata.labels.environment }}/overrides.yaml - - $overlay/{{ .metadata.annotations.overlay_repo_basepath }}overlays/clusters/{{ .nameNormalized }}/overrides.yaml - {{- end }} + - $overlay/{{ or .metadata.annotations.overlay_repo_basepath .metadata.annotations.addonsRepoBasepath }}overlays/environments/{{ .metadata.labels.environment }}/overrides.yaml + - $overlay/{{ or .metadata.annotations.overlay_repo_basepath .metadata.annotations.addonsRepoBasepath }}overlays/clusters/{{ .nameNormalized }}/overrides.yaml valuesObject: - overlayRepoURLGit: '{{ default "" .metadata.annotations.overlay_repo_url }}' - overlayRepoURLGitRevision: '{{ default "main" .metadata.annotations.overlay_repo_revision }}' - overlayRepoURLGitBasePath: '{{ default "" .metadata.annotations.overlay_repo_basepath }}' + overlayRepoURLGit: '{{ .metadata.annotations.overlay_repo_url }}' + overlayRepoURLGitRevision: '{{ or .metadata.annotations.overlay_repo_revision "main" }}' + overlayRepoURLGitBasePath: '{{ .metadata.annotations.overlay_repo_basepath }}' destination: namespace: argocd name: '{{ .name }}'