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
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

.PHONY: dev test unit e2e goldens \
.PHONY: dev test unit e2e real-e2e goldens \
k3d-start k3d-stop k3d-restart k3d-delete \
argocd-up argocd-down argocd-restart \
argocd-portforward argocd-portforward-stop \
Expand Down Expand Up @@ -41,6 +41,11 @@ unit:
e2e:
go test -tags e2e ./e2e -v -count=1 -parallel $(PARALLEL)

# Run the horizontal e2e suite against the local ArgoCD (see argocd/fixtures).
# Requires: make argocd-up && make argocd-git-daemon && ./argocd/fixtures/seed-sync-fixtures.sh
real-e2e:
ARGONAUT_REAL_ARGOCD=1 go test -tags e2e ./e2e -run TestRealArgoCD -v -count=1

# Regenerate golden snapshots for app tests.
goldens:
UPDATE_GOLDEN=1 go test ./cmd/app -run TestGolden_ -v
Expand Down
139 changes: 139 additions & 0 deletions argocd/fixtures/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Sync-option fixtures

Three small Argo CD Applications for exercising sync options against the local
k3d Argo CD.

```bash
make argocd-up
make argocd-git-daemon
./argocd/fixtures/seed-sync-fixtures.sh
```

`seed-sync-fixtures.sh` reuses `scripts/seed-history.sh`'s mechanism: it builds a
throwaway git repo next to the argonaut checkout (`argonaut-sync-fixtures-repo`),
which the `git daemon` from `make argocd-git-daemon` exports, so the cluster
clones it as `git://host.k3d.internal/argonaut-sync-fixtures-repo` with no push
to any remote. Re-running the script deletes the old Applications with
`argocd app delete --yes`, which cascades to managed resources by default,
then rebuilds the repo and syncs the two prune apps. The schema-error app is
left unsynced. The script stops if an old Application remains after 60 seconds,
before rebuilding the repository or applying new Applications.

The script derives the daemon base path from `git rev-parse --show-toplevel`.
Outside a checkout, set `GIT_DAEMON_BASE_PATH`.

The real e2e harness accepts only loopback endpoints (`localhost`, `127.0.0.1`,
or `::1`) and honors the CLI context's `insecure` setting. Use the local context
created by `make argocd-login` for the demo's self-signed certificate.

Run the seed script's isolated regression tests without a cluster:

```bash
python3 -B -m unittest discover -s argocd/fixtures -p '*_test.py'
```

## The fixtures

| App | Namespace | What it demonstrates |
|---|---|---|
| `prune-demo` | `sync-prune-demo` | A live ConfigMap with no git counterpart — `sync --prune` really deletes something |
| `prune-confirm-demo` | `sync-confirm-demo` | `sync --prune` parks in `Running` awaiting confirmation |
| `schema-error-demo` | `sync-invalid-demo` | `sync --dry-run` fails with a validation message |

All three follow the existing conventions: `project: default`,
`destination.server: https://kubernetes.default.svc`, manual sync
(`syncPolicy.automated: null`), `CreateNamespace=true` — same shape as
`argocd/apps-hang.yaml`.

### 1. Something to prune

The seed repo has two commits. The first contains `keep-me` **and**
`prune-me`; the second deletes `prune-me`. The script syncs the app at the
**first** commit, but the Application's `targetRevision` is `main`. So
`prune-me` is live in the cluster and absent from the tracked revision — Argo CD
marks the app OutOfSync and offers it as a prune candidate.

```bash
argocd app sync prune-demo # stays OutOfSync, prune skipped
argocd app sync prune-demo --prune # deletes prune-me, app goes Synced
```

This "manifest set shrinks between commits" trick is the same one
`seed-history.sh` already uses for `manifests/service.yaml` (present only from
commit 2 onward), just run backwards.

### 2. Prune stuck awaiting confirmation

Same setup, plus `argocd.argoproj.io/sync-options: Prune=confirm` on the orphan
ConfigMap. A prune task has no target object, so Argo CD reads the sync option
off the **live** resource — which is why the annotation has to be present in the
commit the app is synced at, not added later.

```bash
argocd app sync prune-confirm-demo --prune --timeout 60 # blocks
argocd app get prune-confirm-demo # operation phase: Running
argocd app confirm-deletion prune-confirm-demo # releases it
```

Notes:
- Without `--prune` the gate never triggers — the app just stays OutOfSync.
- Confirming is also possible from the UI ("Confirm Pruning") or by annotating
the **Application** with `argocd.argoproj.io/deletion-approved: <RFC3339 ts>`.

### 3. Schema error

`manifests/schema-error/deployment.yaml` says `reploicas` instead of `replicas`.
Argo CD's dry-run apply hits the API server, which rejects it:

```
Deployment in version "v1" cannot be handled as a Deployment:
strict decoding error: unknown field "spec.reploicas"
```

```bash
argocd app sync schema-error-demo --dry-run # SyncFailed, no cluster changes
argocd app sync schema-error-demo # same failure, recorded in status
```

The script leaves this app unsynced so the first sync attempt is the failing one.

Use the **plain** sync when the failure has to be visible in argonaut: a real
sync definitely records `SyncFailed` in `status.operationState`, which is what
the TUI renders. Whether `--dry-run` persists an operation result there too, or
only prints to the CLI, was not verified — see below.

## Version requirements

- **`Prune=confirm` (fixture 2) needs Argo CD ≥ 2.14**, not 3.1 as the task
stated — upstream introduced it in the 2.14 release. This box currently runs
**v3.5.1**, so it is well covered.
- `setup-fixed.sh` installs from
`https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml`,
a **moving target**. It cannot pin a version, so the fixture's floor is a
documented assumption rather than an enforced one. It only breaks if `stable`
ever regresses below 2.14, which it will not.
- Fixtures 1 and 3 have no meaningful version floor.
- The local `argocd` CLI is v3.4.2, one minor behind the server. `app
confirm-deletion` is present in it (verified via `argocd app --help`).

## Verified vs. assumed

Verified against the running cluster:
- Server image `quay.io/argoproj/argocd:v3.5.1`; CLI `v3.4.2`.
- `kubectl apply --dry-run=server` rejects `spec.reploicas` with the strict
decoding error quoted above. (`--dry-run=client` silently accepts it — client
side validation is not enough, but Argo CD's sync does a server-side dry run.)
- `argocd app confirm-deletion` exists; `objRequiresPruneConfirmation` and
`WithPruneConfirmed` are present in the shipped binary.

Assumed, not verified end to end:
- That the whole seed script runs green — it was syntax-checked only, never
executed, since that would mutate the live cluster and create a sibling repo
directory.
- The exact wording of the `argocd.argoproj.io/deletion-approved` annotation
against 3.5.1 (taken from upstream docs, not from this install).
- That `--prune` on `prune-confirm-demo` parks in `Running` rather than failing
outright. This is the documented behaviour but was not run here.
- That a **dry-run** sync writes `SyncFailed` into `status.operationState`. Only
the API server's rejection of the manifest was verified, not what Argo CD
persists. A plain sync is the safe variant for anything reading app status.
69 changes: 69 additions & 0 deletions argocd/fixtures/apps-sync-options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Three purpose-built fixtures for exercising sync options against the local
# k3d Argo CD. They all point at the local git daemon repo seeded by
# ./seed-sync-fixtures.sh, so nothing here needs a remote push.
#
# make argocd-up && make argocd-git-daemon
# ./seed-sync-fixtures.sh
#
# Manual sync only, matching argocd/apps-hang.yaml and the other demo apps.
---
# 1) Something real to prune: `prune-me` is live but no longer in git.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prune-demo
namespace: argocd
spec:
project: default
source:
repoURL: git://host.k3d.internal/argonaut-sync-fixtures-repo
targetRevision: main
path: prune
destination:
server: https://kubernetes.default.svc
namespace: sync-prune-demo
syncPolicy:
automated: null # Manual sync — the whole point is to drive prune by hand
syncOptions:
- CreateNamespace=true
---
# 2) Prune gated on confirmation: `argocd app sync prune-confirm-demo --prune`
# parks in Running until `argocd app confirm-deletion prune-confirm-demo`.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prune-confirm-demo
namespace: argocd
spec:
project: default
source:
repoURL: git://host.k3d.internal/argonaut-sync-fixtures-repo
targetRevision: main
path: prune-confirm
destination:
server: https://kubernetes.default.svc
namespace: sync-confirm-demo
syncPolicy:
automated: null # Manual sync
syncOptions:
- CreateNamespace=true
---
# 3) Deliberate schema error: any sync (dry-run included) fails validation.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: schema-error-demo
namespace: argocd
spec:
project: default
source:
repoURL: git://host.k3d.internal/argonaut-sync-fixtures-repo
targetRevision: main
path: schema-error
destination:
server: https://kubernetes.default.svc
namespace: sync-invalid-demo
syncPolicy:
automated: null # Manual sync
syncOptions:
- CreateNamespace=true
6 changes: 6 additions & 0 deletions argocd/fixtures/manifests/prune-confirm/configmap-keep.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: keep-me
data:
role: "permanent"
11 changes: 11 additions & 0 deletions argocd/fixtures/manifests/prune-confirm/configmap-orphan.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Same orphan trick as ../prune, plus the confirmation gate. A prune task has
# no target object, so Argo CD reads the sync option off the LIVE resource —
# the annotation must therefore be present at the commit the app is synced at.
apiVersion: v1
kind: ConfigMap
metadata:
name: prune-me-with-confirmation
annotations:
argocd.argoproj.io/sync-options: Prune=confirm
data:
role: "orphan"
7 changes: 7 additions & 0 deletions argocd/fixtures/manifests/prune/configmap-keep.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Stays in git for the whole fixture — the app should remain Synced on this one.
apiVersion: v1
kind: ConfigMap
metadata:
name: keep-me
data:
role: "permanent"
9 changes: 9 additions & 0 deletions argocd/fixtures/manifests/prune/configmap-orphan.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Removed from git by the second seed commit. After the app is synced at the
# FIRST commit this exists in the cluster with no git counterpart, so a sync
# with --prune has something real to delete.
apiVersion: v1
kind: ConfigMap
metadata:
name: prune-me
data:
role: "orphan"
22 changes: 22 additions & 0 deletions argocd/fixtures/manifests/schema-error/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# `reploicas` is a deliberate typo. The API server rejects it with a strict
# decoding error, which Argo CD's dry-run apply surfaces as SyncFailed.
apiVersion: apps/v1
kind: Deployment
metadata:
name: schema-error
spec:
reploicas: 1
selector:
matchLabels:
app: schema-error
template:
metadata:
labels:
app: schema-error
spec:
containers:
- name: pause
image: registry.k8s.io/pause:3.9
resources:
limits: {cpu: "10m", memory: "16Mi"}
requests: {cpu: "5m", memory: "8Mi"}
Loading
Loading