From f35546fcf8054ac67d8613ae3824e3e8eacf661b Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Fri, 13 Feb 2026 10:22:28 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=A4=96=20feat:=20add=20Argo=20CD=20si?= =?UTF-8?q?ngle-apply=20ApplicationSet=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an Argo CD ApplicationSet example that bootstraps CloudNativePG, coder-k8s, a PostgreSQL cluster, and a CoderControlPlane from one manifest. Also add Argo sync-wave annotations to the existing CloudNativePG example manifests so they apply in dependency order, and document both examples in README files. --- _Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$0.56`_ --- README.md | 1 + examples/argocd/README.md | 64 +++++++++++++++++++ examples/argocd/applicationset.yaml | 40 ++++++++++++ examples/argocd/apps/00-cnpg-operator.yaml | 26 ++++++++ .../argocd/apps/01-coder-k8s-operator.yaml | 30 +++++++++ .../argocd/apps/02-coder-cloudnativepg.yaml | 24 +++++++ examples/cloudnativepg/00-namespace.yaml | 2 + examples/cloudnativepg/README.md | 2 + examples/cloudnativepg/cnpg-cluster.yaml | 3 + examples/cloudnativepg/codercontrolplane.yaml | 3 + 10 files changed, 195 insertions(+) create mode 100644 examples/argocd/README.md create mode 100644 examples/argocd/applicationset.yaml create mode 100644 examples/argocd/apps/00-cnpg-operator.yaml create mode 100644 examples/argocd/apps/01-coder-k8s-operator.yaml create mode 100644 examples/argocd/apps/02-coder-cloudnativepg.yaml diff --git a/README.md b/README.md index e79854ba..53cbcff8 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ make docs-serve ## Examples - [`examples/cloudnativepg/`](examples/cloudnativepg/) — Deploy a `CoderControlPlane` with a CloudNativePG-managed PostgreSQL backend. +- [`examples/argocd/`](examples/argocd/) — Bootstrap CloudNativePG + `coder-k8s` + PostgreSQL + `CoderControlPlane` from one Argo CD `ApplicationSet`. ## KIND development cluster (k9s demos) diff --git a/examples/argocd/README.md b/examples/argocd/README.md new file mode 100644 index 00000000..17a83036 --- /dev/null +++ b/examples/argocd/README.md @@ -0,0 +1,64 @@ +# Argo CD single-apply example (CloudNativePG + coder-k8s) + +This example bootstraps the full stack from one `ApplicationSet`: + +1. CloudNativePG operator +2. `coder-k8s` operator stack (CRDs + RBAC + deployment) +3. CloudNativePG PostgreSQL `Cluster` +4. `CoderControlPlane` + +## Prerequisites + +- A Kubernetes cluster +- `kubectl` configured for that cluster +- Argo CD installed (including the ApplicationSet controller) +- Argo CD v2.6+ (required for Application `sources` in `apps/01-coder-k8s-operator.yaml`) + +## Apply one manifest + +```bash +kubectl apply -f examples/argocd/applicationset.yaml +``` + +That creates: + +- `ApplicationSet` `coder-k8s-stack` +- parent `Application` `coder-k8s-stack` +- child Applications in `examples/argocd/apps/` + +### Ordering behavior + +`apps/00-cnpg-operator.yaml` and `apps/01-coder-k8s-operator.yaml` are synced in wave `0`, and `apps/02-coder-cloudnativepg.yaml` in wave `1`. + +The CloudNativePG example manifests (`examples/cloudnativepg/`) also include resource-level sync waves so PostgreSQL is applied before `CoderControlPlane`. + +## Watch rollout + +```bash +kubectl -n argocd get applications +kubectl -n coder wait --for=condition=Ready cluster/coder-db --timeout=10m +kubectl -n coder rollout status deployment/coder --timeout=10m +``` + +## Access Coder + +```bash +kubectl -n coder port-forward svc/coder 3000:80 +``` + +Open and complete the setup flow. + +## Customization + +- This example tracks `https://github.com/coder/coder-k8s.git` at `main` for child apps. + Update `repoURL` and `targetRevision` in `examples/argocd/apps/*.yaml` if you want to pin to a tag or use a fork. +- `apps/00-cnpg-operator.yaml` uses `targetRevision: "*"` for the CloudNativePG chart. + Pin this to a specific chart version for reproducibility. + +## Cleanup + +```bash +kubectl -n argocd delete applicationset coder-k8s-stack +``` + +Depending on your storage class reclaim policy, PVCs from PostgreSQL may remain after cleanup. diff --git a/examples/argocd/applicationset.yaml b/examples/argocd/applicationset.yaml new file mode 100644 index 00000000..064025dc --- /dev/null +++ b/examples/argocd/applicationset.yaml @@ -0,0 +1,40 @@ +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: coder-k8s-stack + namespace: argocd + labels: + app.kubernetes.io/part-of: coder-k8s-example +spec: + goTemplate: true + goTemplateOptions: + - missingkey=error + generators: + - list: + elements: + - name: coder-k8s-stack + destinationServer: https://kubernetes.default.svc + repoURL: https://github.com/coder/coder-k8s.git + targetRevision: main + path: examples/argocd/apps + template: + metadata: + name: "{{ .name }}" + namespace: argocd + labels: + app.kubernetes.io/part-of: coder-k8s-example + spec: + project: default + source: + repoURL: "{{ .repoURL }}" + targetRevision: "{{ .targetRevision }}" + path: "{{ .path }}" + destination: + server: "{{ .destinationServer }}" + namespace: argocd + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/examples/argocd/apps/00-cnpg-operator.yaml b/examples/argocd/apps/00-cnpg-operator.yaml new file mode 100644 index 00000000..c6e039aa --- /dev/null +++ b/examples/argocd/apps/00-cnpg-operator.yaml @@ -0,0 +1,26 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: cnpg-operator + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "0" + labels: + app.kubernetes.io/part-of: coder-k8s-example +spec: + project: default + source: + repoURL: https://cloudnative-pg.github.io/charts + chart: cloudnative-pg + targetRevision: "*" + helm: + releaseName: cnpg + destination: + server: https://kubernetes.default.svc + namespace: cnpg-system + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/examples/argocd/apps/01-coder-k8s-operator.yaml b/examples/argocd/apps/01-coder-k8s-operator.yaml new file mode 100644 index 00000000..ae9c93ca --- /dev/null +++ b/examples/argocd/apps/01-coder-k8s-operator.yaml @@ -0,0 +1,30 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: coder-k8s-operator + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "0" + labels: + app.kubernetes.io/part-of: coder-k8s-example +spec: + project: default + sources: + - repoURL: https://github.com/coder/coder-k8s.git + targetRevision: main + path: config/crd/bases + - repoURL: https://github.com/coder/coder-k8s.git + targetRevision: main + path: config/rbac + - repoURL: https://github.com/coder/coder-k8s.git + targetRevision: main + path: deploy + destination: + server: https://kubernetes.default.svc + namespace: coder-system + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/examples/argocd/apps/02-coder-cloudnativepg.yaml b/examples/argocd/apps/02-coder-cloudnativepg.yaml new file mode 100644 index 00000000..5eac93b7 --- /dev/null +++ b/examples/argocd/apps/02-coder-cloudnativepg.yaml @@ -0,0 +1,24 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: coder-cloudnativepg + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "1" + labels: + app.kubernetes.io/part-of: coder-k8s-example +spec: + project: default + source: + repoURL: https://github.com/coder/coder-k8s.git + targetRevision: main + path: examples/cloudnativepg + destination: + server: https://kubernetes.default.svc + namespace: coder + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/examples/cloudnativepg/00-namespace.yaml b/examples/cloudnativepg/00-namespace.yaml index 67aec19c..8778e363 100644 --- a/examples/cloudnativepg/00-namespace.yaml +++ b/examples/cloudnativepg/00-namespace.yaml @@ -2,5 +2,7 @@ apiVersion: v1 kind: Namespace metadata: name: coder + annotations: + argocd.argoproj.io/sync-wave: "0" labels: app.kubernetes.io/part-of: coder-k8s-example diff --git a/examples/cloudnativepg/README.md b/examples/cloudnativepg/README.md index 87869a05..892f462d 100644 --- a/examples/cloudnativepg/README.md +++ b/examples/cloudnativepg/README.md @@ -46,6 +46,8 @@ kubectl apply -f examples/cloudnativepg/ The namespace manifest is prefixed (`00-namespace.yaml`) so `kubectl apply -f` creates `coder` before namespaced resources. +These manifests also include Argo CD sync-wave annotations so the same directory works with [`examples/argocd/`](../argocd/): namespace (`wave 0`) -> CloudNativePG `Cluster` (`wave 1`) -> `CoderControlPlane` (`wave 2`). + Wait for PostgreSQL and verify the generated Secret: ```bash diff --git a/examples/cloudnativepg/cnpg-cluster.yaml b/examples/cloudnativepg/cnpg-cluster.yaml index 6cc119d0..2996abc9 100644 --- a/examples/cloudnativepg/cnpg-cluster.yaml +++ b/examples/cloudnativepg/cnpg-cluster.yaml @@ -3,6 +3,9 @@ kind: Cluster metadata: name: coder-db namespace: coder + annotations: + argocd.argoproj.io/sync-wave: "1" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: instances: 1 storage: diff --git a/examples/cloudnativepg/codercontrolplane.yaml b/examples/cloudnativepg/codercontrolplane.yaml index 6f0a9a8f..e6b425d0 100644 --- a/examples/cloudnativepg/codercontrolplane.yaml +++ b/examples/cloudnativepg/codercontrolplane.yaml @@ -3,6 +3,9 @@ kind: CoderControlPlane metadata: name: coder namespace: coder + annotations: + argocd.argoproj.io/sync-wave: "2" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true spec: replicas: 1 service: From f1081fcc67a7348ccc8e8c5aeb1c0ff45b3c9c27 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Fri, 13 Feb 2026 10:32:29 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=A4=96=20fix:=20remove=20app-of-apps?= =?UTF-8?q?=20ordering=20ambiguity=20in=20Argo=20example?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the Argo CD example so the ApplicationSet generates one multi-source Application instead of a parent app that applies child Application resources. This avoids cross-Application wave ordering ambiguity, adds a cascading resources finalizer for cleanup, and keeps dependency ordering at the resource level via sync waves. --- _Generated with [`mux`](https://github.com/coder/mux) • Model: `openai:gpt-5.3-codex` • Thinking: `xhigh` • Cost: `$0.56`_ --- examples/argocd/README.md | 34 +++++++++++-------- examples/argocd/applicationset.yaml | 32 +++++++++++++---- examples/argocd/apps/00-cnpg-operator.yaml | 26 -------------- .../argocd/apps/01-coder-k8s-operator.yaml | 30 ---------------- .../argocd/apps/02-coder-cloudnativepg.yaml | 24 ------------- .../resources/00-coder-system-namespace.yaml | 8 +++++ 6 files changed, 54 insertions(+), 100 deletions(-) delete mode 100644 examples/argocd/apps/00-cnpg-operator.yaml delete mode 100644 examples/argocd/apps/01-coder-k8s-operator.yaml delete mode 100644 examples/argocd/apps/02-coder-cloudnativepg.yaml create mode 100644 examples/argocd/resources/00-coder-system-namespace.yaml diff --git a/examples/argocd/README.md b/examples/argocd/README.md index 17a83036..c1533d3b 100644 --- a/examples/argocd/README.md +++ b/examples/argocd/README.md @@ -1,9 +1,11 @@ # Argo CD single-apply example (CloudNativePG + coder-k8s) -This example bootstraps the full stack from one `ApplicationSet`: +This example bootstraps the full stack from one `ApplicationSet`. -1. CloudNativePG operator -2. `coder-k8s` operator stack (CRDs + RBAC + deployment) +The generated Argo CD `Application` uses multiple sources to deploy: + +1. CloudNativePG operator (Helm chart) +2. `coder-k8s` operator stack (`config/crd/bases`, `config/rbac`, `deploy`) 3. CloudNativePG PostgreSQL `Cluster` 4. `CoderControlPlane` @@ -12,7 +14,7 @@ This example bootstraps the full stack from one `ApplicationSet`: - A Kubernetes cluster - `kubectl` configured for that cluster - Argo CD installed (including the ApplicationSet controller) -- Argo CD v2.6+ (required for Application `sources` in `apps/01-coder-k8s-operator.yaml`) +- Argo CD v2.6+ (required for `spec.sources` in the generated Application) ## Apply one manifest @@ -23,14 +25,18 @@ kubectl apply -f examples/argocd/applicationset.yaml That creates: - `ApplicationSet` `coder-k8s-stack` -- parent `Application` `coder-k8s-stack` -- child Applications in `examples/argocd/apps/` +- generated `Application` `coder-k8s-stack` + +## Ordering behavior -### Ordering behavior +This setup avoids app-of-apps ordering ambiguity by using a single generated `Application` with multiple sources. -`apps/00-cnpg-operator.yaml` and `apps/01-coder-k8s-operator.yaml` are synced in wave `0`, and `apps/02-coder-cloudnativepg.yaml` in wave `1`. +Resource-level sync waves provide dependency ordering for workload resources: -The CloudNativePG example manifests (`examples/cloudnativepg/`) also include resource-level sync waves so PostgreSQL is applied before `CoderControlPlane`. +- `examples/argocd/resources/00-coder-system-namespace.yaml` uses `wave -1` +- `examples/cloudnativepg/00-namespace.yaml` uses `wave 0` +- `examples/cloudnativepg/cnpg-cluster.yaml` uses `wave 1` +- `examples/cloudnativepg/codercontrolplane.yaml` uses `wave 2` ## Watch rollout @@ -50,10 +56,10 @@ Open and complete the setup flow. ## Customization -- This example tracks `https://github.com/coder/coder-k8s.git` at `main` for child apps. - Update `repoURL` and `targetRevision` in `examples/argocd/apps/*.yaml` if you want to pin to a tag or use a fork. -- `apps/00-cnpg-operator.yaml` uses `targetRevision: "*"` for the CloudNativePG chart. - Pin this to a specific chart version for reproducibility. +- This example tracks `https://github.com/coder/coder-k8s.git` at `main`. + Update `repoURL` and `targetRevision` in `examples/argocd/applicationset.yaml` if you want to pin to a tag or use a fork. +- The CloudNativePG chart version is configurable with `cloudnativepgChartVersion`. + Pin it to an explicit chart version for reproducible environments. ## Cleanup @@ -61,4 +67,4 @@ Open and complete the setup flow. kubectl -n argocd delete applicationset coder-k8s-stack ``` -Depending on your storage class reclaim policy, PVCs from PostgreSQL may remain after cleanup. +The generated `Application` includes `resources-finalizer.argocd.argoproj.io` so deleting the `ApplicationSet` cascades cleanup of managed resources. Depending on your storage class reclaim policy, PVCs from PostgreSQL may remain after cleanup. diff --git a/examples/argocd/applicationset.yaml b/examples/argocd/applicationset.yaml index 064025dc..187479e4 100644 --- a/examples/argocd/applicationset.yaml +++ b/examples/argocd/applicationset.yaml @@ -14,24 +14,44 @@ spec: elements: - name: coder-k8s-stack destinationServer: https://kubernetes.default.svc + destinationNamespace: cnpg-system repoURL: https://github.com/coder/coder-k8s.git targetRevision: main - path: examples/argocd/apps + cloudnativepgChartVersion: "*" template: metadata: name: "{{ .name }}" namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io labels: app.kubernetes.io/part-of: coder-k8s-example spec: project: default - source: - repoURL: "{{ .repoURL }}" - targetRevision: "{{ .targetRevision }}" - path: "{{ .path }}" + sources: + - repoURL: https://cloudnative-pg.github.io/charts + chart: cloudnative-pg + targetRevision: "{{ .cloudnativepgChartVersion }}" + helm: + releaseName: cnpg + - repoURL: "{{ .repoURL }}" + targetRevision: "{{ .targetRevision }}" + path: examples/argocd/resources + - repoURL: "{{ .repoURL }}" + targetRevision: "{{ .targetRevision }}" + path: config/crd/bases + - repoURL: "{{ .repoURL }}" + targetRevision: "{{ .targetRevision }}" + path: config/rbac + - repoURL: "{{ .repoURL }}" + targetRevision: "{{ .targetRevision }}" + path: deploy + - repoURL: "{{ .repoURL }}" + targetRevision: "{{ .targetRevision }}" + path: examples/cloudnativepg destination: server: "{{ .destinationServer }}" - namespace: argocd + namespace: "{{ .destinationNamespace }}" syncPolicy: automated: prune: true diff --git a/examples/argocd/apps/00-cnpg-operator.yaml b/examples/argocd/apps/00-cnpg-operator.yaml deleted file mode 100644 index c6e039aa..00000000 --- a/examples/argocd/apps/00-cnpg-operator.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: cnpg-operator - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "0" - labels: - app.kubernetes.io/part-of: coder-k8s-example -spec: - project: default - source: - repoURL: https://cloudnative-pg.github.io/charts - chart: cloudnative-pg - targetRevision: "*" - helm: - releaseName: cnpg - destination: - server: https://kubernetes.default.svc - namespace: cnpg-system - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/examples/argocd/apps/01-coder-k8s-operator.yaml b/examples/argocd/apps/01-coder-k8s-operator.yaml deleted file mode 100644 index ae9c93ca..00000000 --- a/examples/argocd/apps/01-coder-k8s-operator.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: coder-k8s-operator - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "0" - labels: - app.kubernetes.io/part-of: coder-k8s-example -spec: - project: default - sources: - - repoURL: https://github.com/coder/coder-k8s.git - targetRevision: main - path: config/crd/bases - - repoURL: https://github.com/coder/coder-k8s.git - targetRevision: main - path: config/rbac - - repoURL: https://github.com/coder/coder-k8s.git - targetRevision: main - path: deploy - destination: - server: https://kubernetes.default.svc - namespace: coder-system - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/examples/argocd/apps/02-coder-cloudnativepg.yaml b/examples/argocd/apps/02-coder-cloudnativepg.yaml deleted file mode 100644 index 5eac93b7..00000000 --- a/examples/argocd/apps/02-coder-cloudnativepg.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: coder-cloudnativepg - namespace: argocd - annotations: - argocd.argoproj.io/sync-wave: "1" - labels: - app.kubernetes.io/part-of: coder-k8s-example -spec: - project: default - source: - repoURL: https://github.com/coder/coder-k8s.git - targetRevision: main - path: examples/cloudnativepg - destination: - server: https://kubernetes.default.svc - namespace: coder - syncPolicy: - automated: - prune: true - selfHeal: true - syncOptions: - - CreateNamespace=true diff --git a/examples/argocd/resources/00-coder-system-namespace.yaml b/examples/argocd/resources/00-coder-system-namespace.yaml new file mode 100644 index 00000000..5b7b3efd --- /dev/null +++ b/examples/argocd/resources/00-coder-system-namespace.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: coder-system + annotations: + argocd.argoproj.io/sync-wave: "-1" + labels: + app.kubernetes.io/part-of: coder-k8s-example