Skip to content

ci(longhaul): add build/deploy/monitor workflows + lint & functional smoke gates#413

Open
WentingWu666666 wants to merge 15 commits into
documentdb:mainfrom
WentingWu666666:developer/wentingwu/longhaul-cicd
Open

ci(longhaul): add build/deploy/monitor workflows + lint & functional smoke gates#413
WentingWu666666 wants to merge 15 commits into
documentdb:mainfrom
WentingWu666666:developer/wentingwu/longhaul-cicd

Conversation

@WentingWu666666

@WentingWu666666 WentingWu666666 commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Builds on PR #405 (long-haul driver core, merged).

Adds the GitHub Actions plumbing + Kubernetes manifests to build, deploy,
monitor, and auto-upgrade the long-haul test driver on the dedicated
AKS cluster, plus two CI gates (static lint + functional smoke) that keep the
long-haul workflows and driver honest on every PR.

Note: The plan originally split this into two PRs (PR-3 workflows + PR-4 upgrade). Since the upgrade logic is contained entirely in one file (longhaul-monitor.yaml) and all the Go-side dependencies (operations/upgrade.go, K8sClusterClient.UpgradeDocumentDB, schemaVersion: "auto" in setup.yaml) already shipped in PR #405, splitting added review overhead with no benefit. Consolidated into this PR — long-haul story now lands in 3 PRs instead of 5.

What this PR adds

Workflows (.github/workflows/)

Workflow Trigger Purpose
longhaul-image-build.yml push to main (paths: test/longhaul/**), workflow_dispatch Build test/longhaul/Dockerfile, push to GHCR with :sha-<short> (immutable) + :main tags
longhaul-deploy.yml workflow_run on successful build (auto-deploys :sha-<short>), workflow_dispatch (manual rollback) kubectl apply Deployment manifest, set image, wait for rollout
longhaul-monitor.yaml hourly cron + workflow_dispatch (1) Poll Deployment health, report ConfigMap freshness (≤2h), test result ≠ FAIL. (2) Auto-upgrade operator via Helm if a newer GHCR tag is available, with post-upgrade verification + CRD re-apply. (3) Publish the latest DocumentDB GHCR tag into the longhaul-versions ConfigMap; the driver performs the in-band DocumentDB upgrade as a load-aware operation so continuous writers/verifiers can catch any data-integrity regressions.
lint-longhaul-workflows.yml pull_request (paths: long-haul workflows + test/longhaul/** + test/shared/**), workflow_dispatch Static gate. actionlint + yamllint on the long-haul workflows, go vet/go build/go test on the driver module, kubeconform + Helm-template smoke on the manifests. Catches YAML/lint/compile regressions before they reach the cluster.
longhaul-smoke.yml pull_request (paths: test/longhaul/**, test/shared/**, the build workflow, .github/actions/**), workflow_dispatch Functional gate. Reuses test-build-and-package.yml to build operator + sidecar from PR source (documentdb/gateway stay on public images via the registry probe), stands up kind + cert-manager + a real DocumentDB through the shared setup-test-environment action, then runs the actual long-haul driver (real deploy/*.yaml manifests, only bounded via ConfigMap overrides) and asserts the driver exits 0 and writes longhaul-report result=PASS.

Manifests (test/longhaul/deploy/)

  • deployment.yaml — single-replica Deployment + tunable ConfigMap; image fields templated (__OWNER__/__IMAGE_TAG__) for the deploy workflow.
  • rbac.yaml — namespace-scoped ServiceAccount/Role/RoleBinding (pods, documentdb.io/dbs, configmaps) + ClusterRole for metrics.k8s.io.

Driver hardening (test/longhaul/workload/)

  • verifier.go — stream the verification scan (cursor-based) instead of buffering the full result set, keeping the driver comfortably within its 256Mi limit under sustained load (measured ~35Mi RSS). Prevents the OOM the earlier buffered scan hit as the collection grew.

CI gates: how they complement each other

lint-longhaul-workflows.yml longhaul-smoke.yml
Kind Static Functional (live cluster)
Speed Fast (seconds–minutes) ~20 min (build + kind bring-up)
Proves YAML valid, Go compiles/tests, manifests schema-valid Driver actually builds, connects to DocumentDB, runs writers+verifier, reports PASS

Setup required before the deploy/monitor workflows can run

Cluster admin one-time bootstrap (the deployer ServiceAccount is namespace-scoped by design):

  1. kubectl apply -f test/longhaul/deploy/setup.yaml (already on main via PR test(longhaul): add long-haul test driver core #405; namespace + DocumentDB CR + credentials placeholder)
  2. kubectl apply -f test/longhaul/deploy/rbac.yaml
  3. Create longhaul-documentdb-credentials secret with key uri (see test/longhaul/README.md)
  4. Issue a long-lived kubeconfig for the longhaul-test ServiceAccount, store as repo secret LONGHAUL_KUBECONFIG

Test plan

  • Lint Long-Haul Workflows — green on PR (actionlint + yamllint + driver go checks + manifest schema/Helm smoke).
  • Long-Haul Smoke Gate — verified green end-to-end on this PR: driver exit 0, longhaul-report result PASS, with the database images reused (not rebuilt) via registry mode. Evidence posted in a PR comment.
  • Manual dry-run of build/deploy/monitor on the long-haul AKS cluster (pending LONGHAUL_KUBECONFIG secret provisioning).

@documentdb-triage-tool documentdb-triage-tool Bot added CI/CD enhancement New feature or request labels Jun 29, 2026
@documentdb-triage-tool

Copy link
Copy Markdown

🤖 Auto-triaged by documentdb-triage-tool.

Applied: CI/CD, enhancement
Project fields suggested: Component ci · Priority P2 · Effort L · Status In Progress
Confidence: 0.30 (deterministic)

Reasoning

component from path globs (ci); effort from diff stats (672+0 LOC, 5 files); LLM failed: Invalid response body while trying to fetch https://api.anthropic.com/v1/messages: Premature close

If a label is wrong, remove it manually and ping @patty-chow so the rules can be tuned. The bot will not re-label items that already have component labels.

Introduces the GitHub Actions plumbing for the long-haul test driver
that landed in PR documentdb#405. Three workflows + two deploy manifests:

* .github/workflows/longhaul-image-build.yml
    Build/push test/longhaul image to GHCR on main push and on demand.
    Tags every run as :sha-<short> (immutable) plus :main.
* .github/workflows/longhaul-deploy.yml
    Roll an image onto the long-haul AKS cluster. Auto-triggered after
    a successful image build (pins to :sha-<short>) and via
    workflow_dispatch for rollbacks. Uses a namespace-scoped kubeconfig
    in the LONGHAUL_KUBECONFIG secret.
* .github/workflows/longhaul-monitor.yaml
    Hourly health poll: Deployment ready, report ConfigMap fresh
    (<=2h), test result != FAIL. Auto-upgrade and DocumentDB version
    publishing are intentionally left out and will land in a separate
    upgrade PR.
* test/longhaul/deploy/deployment.yaml
    Single-replica Deployment + ConfigMap. Image fields templated
    (__OWNER__/__IMAGE_TAG__) for the deploy workflow to substitute.
    Aligned with the post-PR-405 env-var surface
    (LONGHAUL_DOCUMENTDB_URI, no NUM_VERIFIERS) and the credential
    secret name documented in test/longhaul/README.md
    (longhaul-documentdb-credentials with a uri key).
* test/longhaul/deploy/rbac.yaml
    Namespace-scoped ServiceAccount/Role/RoleBinding (pods, dbs,
    configmaps) plus a ClusterRole for metrics.k8s.io.

Splits PR documentdb#348 part 3 of 5. Operator/DocumentDB auto-upgrade plus
post-upgrade verification follow in PR-4.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
@WentingWu666666 WentingWu666666 force-pushed the developer/wentingwu/longhaul-cicd branch from 8b658e6 to db58044 Compare June 30, 2026 17:10
@WentingWu666666 WentingWu666666 changed the title ci(longhaul): add image build, deploy, and monitor workflows (PR #348 split 3/5) ci(longhaul): add image build, deploy, monitor + auto-upgrade workflows (PR #348 split 3/4) Jun 30, 2026
wentingwu000 and others added 3 commits June 30, 2026 13:36
Self-review found 15 issues across the three workflows + deploy
manifest. Fixed in-place; also added a PR-time lint workflow so
future regressions get caught before merge instead of post-merge
against the live cluster.

Critical:
  1. longhaul-image-build: build context was test/longhaul, but the
     module's go.mod has `replace` directives for ../shared and
     ../../operator/src. Set context=. and file=test/longhaul/Dockerfile
     so the replace targets are inside the build context.
  2. longhaul-monitor: operator version check used
     `-l app.kubernetes.io/name=documentdb-operator` + .items[0]; the
     chart applies that label to three Deployments (operator, sidecar
     injector, wal replica). Target the operator Deployment by name
     (env.OPERATOR_DEPLOYMENT=documentdb-operator).
  3. longhaul-monitor: missing `packages: read` permission; the GHCR
     version lookups (`gh api /orgs/.../packages/...`) silently fall
     back to chart defaults without it.
  4. longhaul-deploy: header comment claimed kubeconfig is scoped via
     rbac.yaml. rbac.yaml is the driver pod's in-cluster SA — orthogonal
     to the GHA kubeconfig, which the upgrade job needs cluster-admin
     for (helm upgrade in another ns + CRD apply). Rewrote header.

Major:
  5. All three workflows: secrets.LONGHAUL_KUBECONFIG was inlined into
     `run:` blocks via ${{ secrets.* }}. Switch to env: passthrough so
     `$` and backticks in the secret cannot shell-inject.
  6. longhaul-monitor: ConfigMap longhaul-versions was applied with
     `kubectl create --dry-run | kubectl apply -f -`, which sends a full
     body and removes any keys the driver may have added (last-applied,
     attempt counters, etc.). Switch to server-side apply with
     --field-manager=longhaul-monitor so the monitor co-owns only the
     fields it sets.
  7. longhaul-deploy: redundant `kubectl set image` + always
     rollout-restart after `kubectl apply` which already rolls a pod
     when the image changes. Dropped the set-image; gate the restart on
     mutable tags only (main / latest / sha-*); pinned semver tags
     never need a forced restart.
  8. (deferred) workflow_run only fires on default branch — covered by
     the new lint-longhaul-workflows.yml which runs on PRs.
  9. (verified, not a bug) GHCR documentdb image path matches the
     URL-encoded subpackage form used in the operator source
     (operator/src/internal/utils/constants.go).

Minor:
 10. Pinned runs-on to ubuntu-22.04 across all jobs (was ubuntu-latest
     in places) so future runner image roll-forward doesn't surprise us.
 11. Added concurrency: groups to all three workflows (cancel-in-progress
     true for build/lint, false for deploy/monitor to avoid mid-rollout
     cancellation).
 12. Added timeout-minutes to every job.
 13. longhaul-image-build: replaced
     cache-{from,to}=type=registry,ref=...:buildcache (accumulates in
     GHCR, no GC) with type=gha so cache cycles automatically.
 14. longhaul-monitor: added "Dump driver logs on failure" step that
     tails 200 lines from the long-haul pod + describes it whenever any
     prior step in check-longhaul fails. Run summaries now contain
     enough triage context without a kubectl session.
 15. test/longhaul/deploy/deployment.yaml ConfigMap was missing the
     tunables LONGHAUL_STEADY_STATE_WAIT, LONGHAUL_REPORT_INTERVAL,
     LONGHAUL_RESET_DATA. Added with explicit values (60s, 1h, false)
     that match the in-code defaults and commentary about when to
     override.

New CI gating:
  .github/workflows/lint-longhaul-workflows.yml runs on PRs touching
  any of the long-haul workflow files, Dockerfile, deploy manifests, or
  the operator chart:
    - actionlint (+ embedded shellcheck on run: blocks)
    - yamllint (strict)
    - kubeconform against k8s 1.30 schemas + a Datree CRD catalog
      fallback (skips DocumentDB which we don't ship a CRD schema for)
    - helm template smoke against operator/documentdb-helm-chart with a
      grep assertion that the rendered output still contains a
      Deployment named exactly `documentdb-operator` — protects against
      regressions of the fix in (2)
    - docker buildx with push:false, mirroring production build exactly
      to catch context/replace-directive bugs at PR time

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
- lint-longhaul-workflows.yml: replace deprecated reviewdog input
  fail_on_error with fail_level: error.
- Group consecutive $GITHUB_OUTPUT redirects with { ...; } >> file
  to satisfy shellcheck SC2129 (image-build, deploy, monitor).
- monitor: bind ready output to a shell variable before the numeric
  -lt comparison to satisfy shellcheck SC2170.
- image-build: drop extra spaces after cache-to: colon (yamllint).
- monitor: use two spaces before inline comments (yamllint).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Build chart dependencies (cloudnative-pg) before templating and pass
--api-versions cert-manager.io/v1/Certificate so the cert-manager
preflight check passes during offline rendering.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
@WentingWu666666 WentingWu666666 changed the title ci(longhaul): add image build, deploy, monitor + auto-upgrade workflows (PR #348 split 3/4) ci(longhaul): add image build, deploy, monitor + auto-upgrade workflows Jun 30, 2026
The 'Publish desired DocumentDB version' step performs a server-side apply
on the longhaul-versions ConfigMap with field manager 'longhaul-monitor'.
When the ConfigMap already exists and its fields (e.g. .data.last-updated)
are owned by another manager such as 'kubectl-client-side-apply' (from an
earlier 'kubectl create'/client-side apply), the SSA fails with a field
ownership conflict:

  error: Apply failed with 1 conflict: conflict with
  "kubectl-client-side-apply" using v1: .data.last-updated

Add --force-conflicts so the monitor, which is the intended authoritative
writer of desired-documentdb-version and last-updated, cleanly takes
ownership of those fields.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
@WentingWu666666

Copy link
Copy Markdown
Collaborator Author

✅ End-to-end verification evidence

All four long-haul workflows have been exercised against a live AKS cluster (longhaul-aks, eastus2) and are green. Runs below are on my fork (public, so links are viewable).

Workflow Result Run
Lint Long-Haul Workflows (PR gate: lint + image-build-smoke) ✅ pass checks on this PR
LONGHAUL – Build Test Driver Image ✅ pass run 28875853574
LONGHAUL – Deploy Test Driver to AKS ✅ pass run 28876113977
Long Haul Monitor (check-longhaul + upgrade-if-needed) ✅ pass run 28876022267
DCO ✅ pass

What each run proves

  • Build – multi-stage image builds from repo root context and pushes to GHCR (longhaul-test:sha-01e5032, :main).
  • Deploy – applies the driver Deployment to AKS, waits for rollout, verifies pod health.
  • Monitor – reads the driver's longhaul-report ConfigMap, validates freshness + result, and the upgrade-if-needed job reconciles the operator and publishes the desired DocumentDB version via server-side apply (the --force-conflicts fix in 01e5032 is exercised here).

Live cluster state at time of run

  • DocumentDB CR: "Cluster in healthy state", documentdb-cluster-1 2/2 Running.
  • Latest driver report: Result: PASS — Writes 14,735 / Ack 14,735 (100% success), 0 gaps, 0 checksum errors.

Note: the monitor's check-longhaul job correctly failed on a first attempt when the report was stale (>2h), demonstrating the staleness guard works; it passed once a fresh report was published.

wentingwu000 and others added 3 commits July 7, 2026 11:01
The lint-longhaul-workflows.yml file was missing the standard MIT
license header present in the other longhaul workflow files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Strengthen the pre-merge long-haul gate:

- Broaden the PR path filter to test/longhaul/** so edits to the driver
  sources (not just Dockerfile/deploy) trigger the gate. Previously a
  change under test/longhaul/workload, operations, report, etc. would
  skip the build-smoke and tests entirely.
- Add a go-test job that runs 'go vet ./...' and 'go test ./...' for the
  driver module. The driver has real unit tests (config, journal,
  monitor, operations, report) that the image build only compiled, never
  executed, so logic regressions could slip through.
- Add a manifest sanity guard enforcing a 512Mi memory-limit floor on the
  driver Deployment, and bump the manifest from the 256Mi limit (which
  OOM-kills the driver) to 1Gi. Schema validation cannot catch this.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
The verifier's fetchDocs materialised an entire per-cycle window of
documents into a slice before auditing them. Steady-state that window is
small, but after a disruption pause the verifier catches up on tens of
thousands of docs at once; the slice plus BSON-decode buffers spike the
heap and OOM-kill the driver against a hard 256Mi cgroup limit — even
though the driver's own bookkeeping is only a few MB.

Refactor the audit into an incremental auditor (step/finish) and stream
the cursor through it one doc at a time, so verify memory is O(1) per
cycle instead of O(window). auditDocs is retained as a thin wrapper so
the existing table tests are unchanged. Also surface cursor.Err() so a
truncated read is reported instead of being silently mistaken for tail
loss.

With the streaming scan the driver is genuinely lightweight, so:
- restore the Deployment memory limit to 256Mi (request 128Mi),
- add GOMEMLIMIT=200MiB as a soft GC ceiling below the hard limit, and
- drop the now-unnecessary memory-floor guard from the lint workflow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
@WentingWu666666

Copy link
Copy Markdown
Collaborator Author

🪶 Follow-up: driver is now genuinely lightweight (256Mi, was 1Gi)

While gathering evidence I hit repeated OOMKills of the driver and initially bumped its limit to 1Gi. That was masking a symptom, not fixing the cause — a write-load driver shouldn't need 1Gi. Root-caused and fixed properly:

Root cause

workload/verifier.go:fetchDocs materialised an entire per-cycle scan window into a []WriteDocument slice before auditing it. Steady-state that window is tiny, but after a disruption pause the verifier catches up on tens of thousands of docs at once; the slice + BSON-decode buffers spike the heap and OOM against a hard 256Mi cgroup limit — even though the driver's own bookkeeping is only a few MB.

Fix (commit d032131)

  • Stream the scan: extracted an incremental auditor (step/finish) and feed cursor-decoded docs through it one at a time, so verify memory is O(1) per cycle instead of O(window). auditDocs is retained as a thin wrapper, so all existing table tests are unchanged.
  • Also fixed a latent bug: now surface cursor.Err() so a truncated read is reported instead of being silently mistaken for tail loss (a false positive).
  • Restore 256Mi limit / 128Mi request, and add GOMEMLIMIT=200MiB as a soft GC ceiling below the hard limit (belt-and-suspenders).
  • Dropped the temporary memory-floor lint guard — unnecessary once the cause is fixed.

Evidence (live AKS, streaming image sha-d032131 at 256Mi)

  • Driver Running, 0 restarts, GOMEMLIMIT=200MiB applied, limit 256Mi.
  • kubectl top steady at ~35Mi RSS (≈14% of the 256Mi limit) under active 5-writer load.
  • All PR checks green (lint, go-test, image-build-smoke, DCO); build + deploy workflows green against the live cluster.

Steady-state is proven here; the disruption-catch-up spike is eliminated at the source (O(1) scan) and further guarded by GOMEMLIMIT. The post-merge AKS long-haul remains the endurance/scale proof.

wentingwu000 and others added 4 commits July 7, 2026 11:47
Add longhaul-smoke.yml, a PR gate that stands up a real DocumentDB on an
ephemeral kind cluster and runs the actual long-haul driver against it for
a short bounded window. It reuses test/longhaul/deploy/{deployment,rbac}.yaml
unmodified (only patching runtime knobs on the shipped ConfigMap for a
bounded, deterministic run) so manifest/driver regressions fail the PR.

Flow: build driver image -> kind -> cert-manager -> CSI hostpath ->
operator via public OCI chart -> single-instance ClusterIP DocumentDB ->
run driver (LONGHAUL_MAX_DURATION bounded, scale ops disabled) -> assert
container exit 0 AND longhaul-report ConfigMap result == PASS.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Refactor longhaul-smoke.yml to delegate environment bring-up (kind,
cert-manager, CSI, operator install, DocumentDB deploy + readiness) to the
shared .github/actions/setup-test-environment composite action instead of
duplicating that logic inline.

To make the action usable on a fork PR without building operator/database
images, add a 'released-chart' fast path: when released-chart-version is set,
skip the operator/sidecar image download/verify/kind-load steps and omit the
DocumentDB CR image block so the released operator uses its own tested default
(public) documentdb/gateway images. This is fully backward compatible — the
new guards are keyed on released-chart-version, which existing callers
(test-e2e.yml) never set, so their behavior is unchanged.

The smoke job then builds only the lightweight driver image, loads it into the
action's kind cluster, wires the connection secret, applies the real
deploy/{rbac,deployment}.yaml (patching only ConfigMap runtime knobs for a
bounded run), and asserts container exit 0 AND longhaul-report result == PASS.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Rewrite longhaul-smoke.yml to mirror test-e2e.yml: a reusable build job
(test-build-and-package.yml) produces operator+sidecar from PR source while
documentdb/gateway stay on public images via registry-mode probe, then the
smoke job loads those artifacts into kind through the shared
setup-test-environment action (use-external-images: false) and runs the real
long-haul driver against a real DocumentDB for a bounded window, asserting
exit 0 + longhaul-report result=PASS.

Revert the earlier setup-test-environment/action.yml changes: the released-chart
guards were only needed for fork verification, which is no longer the approach.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
The smoke run reached the gateway but the driver's TLS handshake failed with
'x509: certificate signed by unknown authority'. NewFromURI relies solely on
URI params for TLS, and the Go mongo driver v2 honors tlsInsecure=true (not
tlsAllowInvalidCertificates) to skip verification of the self-signed gateway
cert. Switch the URI to tlsInsecure=true and drop the redundant replicaSet
param, matching the proven form in test/longhaul/README.md.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
@WentingWu666666

Copy link
Copy Markdown
Collaborator Author

✅ Long-Haul Smoke Gate — verified green on this PR

The new longhaul-smoke.yml gate ran end-to-end on this PR's HEAD (050ab45) and passed:

Run: https://github.com/documentdb/documentdb-kubernetes-operator/actions/runs/28883342342Long-Haul Smoke job ✅

Final verdict (Assert PASS step):

Driver exit code : 0
Report result    : PASS
✅ Long-haul smoke gate passed (exit 0, report PASS).

What this proves (real, not static):

  • The reusable build (test-build-and-package.yml) built operator + sidecar from this PR's source, while the database images were NOT rebuilt — the registry probe found the public documentdb/gateway images and Build DocumentDB Packages was skipped (registry mode).
  • The shared setup-test-environment action stood up kind + cert-manager + the built operator + a real DocumentDB cluster (use-external-images: false, artifacts loaded into kind).
  • The actual long-haul driver (the same test/longhaul/deploy/{rbac,deployment}.yaml manifests shipped for the long-haul cluster — only the image ref substituted and a few ConfigMap knobs patched for a bounded 3-minute run) connected to DocumentDB over the gateway (TLS), ran its writers + verifier, exited 0, and wrote longhaul-report with result=PASS.

Build stage (registry mode) job results:

Job Result
Probe Released DocumentDB Images ✅ success
Build Operator Images (amd64) ✅ success
Build Sidecar Images (amd64) ✅ success
Build DocumentDB Packages ⏭️ skipped (public images reused)

This complements the static Lint Long-Haul Workflows gate with a functional check that the driver + deploy manifests actually work against a live operator build.

@WentingWu666666 WentingWu666666 marked this pull request as ready for review July 7, 2026 17:07
@WentingWu666666 WentingWu666666 requested a review from xgerman as a code owner July 7, 2026 17:07
Copilot AI review requested due to automatic review settings July 7, 2026 17:07
@WentingWu666666 WentingWu666666 changed the title ci(longhaul): add image build, deploy, monitor + auto-upgrade workflows ci(longhaul): add build/deploy/monitor workflows + lint & functional smoke gates Jul 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds the CI/CD and Kubernetes plumbing to build, deploy, validate, and continuously monitor the long-haul test driver, alongside a workload verifier change to reduce memory usage by streaming Mongo cursor results.

Changes:

  • Add GitHub Actions workflows for long-haul image build, AKS deploy, health monitoring + auto-upgrade, PR-time linting, and a kind-based smoke gate.
  • Add long-haul Deployment + RBAC manifests for running the driver on the dedicated cluster.
  • Refactor the verifier’s audit path to support O(1) memory scanning via a streaming auditor.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
test/longhaul/workload/verifier.go Refactors verification to stream cursor results through an incremental auditor, avoiding buffering large doc windows.
test/longhaul/deploy/rbac.yaml Introduces ServiceAccount/Role(+Binding) and metrics ClusterRole(+Binding) for the long-haul driver.
test/longhaul/deploy/deployment.yaml Adds ConfigMap-driven configuration and a single-replica Deployment for the long-haul driver.
.github/workflows/longhaul-smoke.yml Adds a PR gate that runs the driver end-to-end on kind for a short bounded window.
.github/workflows/longhaul-monitor.yaml Adds an hourly monitor that checks driver health, auto-upgrades the operator via Helm, and publishes the desired DocumentDB version.
.github/workflows/longhaul-image-build.yml Adds a main-branch workflow to build/push the long-haul driver image to GHCR with immutable and rolling tags.
.github/workflows/longhaul-deploy.yml Adds auto-deploy (post-build) and manual deploy/rollback to the AKS long-haul cluster.
.github/workflows/lint-longhaul-workflows.yml Adds PR-time static validation (actionlint/yamllint/kubeconform/helm template), long-haul Go tests, and a no-push image build smoke test.

Comment thread test/longhaul/deploy/rbac.yaml Outdated
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

# ServiceAccount for the long-haul test Job.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in dd49e83. Comment now reads "ServiceAccount for the long-haul test driver Deployment" (the driver runs as a Deployment, not a Job).

Comment thread test/longhaul/deploy/deployment.yaml Outdated
Comment on lines +10 to +11
# one-line image rolls. The driver itself is restart-safe: it persists
# journal/checkpoint state to PVC and resumes from it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct — there is no PVC (the manifest mounts no volumes). Fixed in dd49e83: the comment now says the driver resumes each writer's sequence from the DocumentDB workload collection and checkpoints to the longhaul-report ConfigMap (no PVC).

Comment thread test/longhaul/deploy/deployment.yaml Outdated
Comment on lines +13 to +14
# Strategy: Recreate (single-replica stateful driver — never run two
# pods concurrently against the same PVC).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — reframed in dd49e83: "never run two driver pods concurrently against the same DocumentDB cluster / workload collection" (the Recreate rationale is about avoiding concurrent drivers, not a PVC).

Comment thread test/longhaul/deploy/deployment.yaml Outdated
Comment on lines +54 to +56
# Whether to drop the workload collection on startup. MUST stay "false"
# on the long-haul cluster — restarts of the driver pod (rolling image
# updates, node drains) need to resume durability state from PVC.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dd49e83 — the LONGHAUL_RESET_DATA comment now points at the actual persistence model: restarts resume durability state from the existing DocumentDB workload collection and longhaul-report ConfigMap checkpoints, not a PVC.

Comment thread test/longhaul/deploy/deployment.yaml Outdated
containers:
- name: driver
image: ghcr.io/__OWNER__/documentdb-kubernetes-operator/longhaul-test:__IMAGE_TAG__
imagePullPolicy: IfNotPresent

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid — for the :main rolling tag a node could serve a stale cached image. Fixed in dd49e83: imagePullPolicy: Always. Pull cost is negligible for a single long-lived driver and it guarantees mutable tags are refreshed.

echo "| Immutable | \`${{ steps.refs.outputs.image }}:sha-${{ steps.refs.outputs.short_sha }}\` |"
echo "| Rolling | \`${{ steps.refs.outputs.image }}:main\` |"
echo ""
echo "Deploy by setting the Job spec image to the immutable tag."

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dd49e83 — step summary now says "Deploy by setting the driver Deployment image to the immutable tag."

Comment thread .github/workflows/longhaul-monitor.yaml Outdated
# Long Haul Test Monitor
#
# Polls the AKS cluster every hour to:
# 1. Check health: Job status, report staleness, test result

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dd49e83 — header now reads "Check health: Deployment status, report staleness, test result."

Comment thread .github/workflows/longhaul-monitor.yaml Outdated
# performs the upgrade in-band as a load-aware operation, so continuous
# writers/verifiers can catch any data-integrity issues caused by it.
#
# Fails if: Job failed, ConfigMap reports FAIL, report is stale (>2h), or Job not found.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dd49e83 — the "Fails if" line now describes it accurately: fails if the driver Deployment is unhealthy, the report ConfigMap reports FAIL, the report is stale (>2h), or the report ConfigMap is not found.

Comment on lines +234 to +237
helm upgrade documentdb-operator operator/documentdb-helm-chart \
-n "${{ env.OPERATOR_NAMESPACE }}" \
--set image.tag="$EXPECTED" \
--wait --timeout 5m

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed and fixed in dd49e83. charts/ and Chart.lock are gitignored, so the cloudnative-pg dependency isn't vendored — a fresh CI checkout would fail the helm upgrade. Added helm dependency update operator/documentdb-helm-chart immediately before the upgrade.

Comment thread .github/workflows/longhaul-deploy.yml Outdated
- name: Verify cluster access
run: |
set -euo pipefail
kubectl version --client=false --output=yaml | head -20 || true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't the || true here make this pass even if you couldn't access the cluster?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch — yes, || true made the gate pass even against an unreachable cluster. Fixed in dd49e83: dropped || true so the step fails fast on a bad/expired kubeconfig before the apply/rollout steps run.

wentingwu000 and others added 3 commits July 8, 2026 19:37
…age pull policy, doc accuracy

Addresses review comments on documentdb#413:

- deploy: drop `|| true` from the "Verify cluster access" step so it fails
  fast on an unreachable/expired kubeconfig instead of silently proceeding
  to apply/rollout (alaye-ms).
- monitor: run `helm dependency update` before `helm upgrade`; the chart's
  cloudnative-pg dependency is gitignored (charts/, Chart.lock), so the
  upgrade would fail on a fresh CI checkout.
- deployment: set imagePullPolicy to Always so mutable tags (e.g. :main)
  are not silently served from a stale node cache.
- deployment/rbac/monitor/image-build: correct leftover "Job"/"PVC" design
  language — the driver runs as a Deployment (no PVC); it resumes writer
  sequences from the DocumentDB workload collection and checkpoints to the
  longhaul-report ConfigMap.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
… limits

The kubeconform step downloads standard Kubernetes JSON schemas from
raw.githubusercontent.com, which intermittently returns HTTP 429 to shared
CI runner IPs. Both the initial run and a re-run failed purely on schema
downloads (Invalid: 0 in every attempt — no actual manifest violations).

Make the gate robust:
- Cache the downloaded schemas across runs via actions/cache so steady-state
  lint performs zero schema downloads.
- Pass `-cache` and wrap kubeconform in a retry-with-backoff loop so a cold
  cache hitting a transient 429 recovers instead of failing; the cache lets
  each retry re-fetch only the schemas still missing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
…gate

The shipped driver manifest now uses imagePullPolicy: Always (so the
registry-based long-haul deploy always refreshes mutable tags like :main).
The kind smoke gate side-loads the locally built longhaul-test:smoke image
into the cluster, where it exists on no registry — Always forces a pull that
fails with ErrImagePull and the rollout times out. Override the policy to
IfNotPresent during the smoke deploy so the side-loaded image is used.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI/CD enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants