Files
cozystack/hack/e2e-chainsaw/postgres/chainsaw-test.yaml
T
Andrey KolkovandClaude Fable 5 b897b53107 test(postgres): fix S3 CA secret name in backup roundtrip e2e
The backup roundtrip e2e copied the seaweedfs S3 CA from a Secret named
seaweedfs-system-ca-cert in tenant-root, a name captured off a dev
cluster. On current main the seaweedfs chart pins
fullnameOverride: seaweedfs, so the cert-manager CA Certificate's secret
is seaweedfs-ca-cert; the old name is absent and the first CI run of the
e2e failed at step 05a with 'secrets "seaweedfs-system-ca-cert" not
found'.

Default S3_CA_SECRET to seaweedfs-ca-cert and, when that Secret is
absent, auto-discover the seaweedfs CA Certificate (the seaweedfs-labelled
one with spec.isCA=true) and read its secretName, so a future
fullnameOverride change does not silently break the copy again. Fail with
a clear message when neither resolves. README and chainsaw comment
updated to match.

Assisted-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
2026-07-20 22:31:27 +04:00

211 lines
7.5 KiB
YAML

# Chainsaw port of hack/e2e-apps/postgres.bats ("Create DB PostgreSQL").
#
# Every `timeout N sh -ec "until kubectl get ..."` + `kubectl wait` pair from
# the BATS test collapses into a single assert: chainsaw polls until the
# resource exists AND matches, or the timeout expires. Failure output is a
# structured diff of the assertion tree against the live object.
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: postgres
spec:
# Diagnostics collected automatically when any step fails. The BATS suite
# only does this in harbor.bats, by hand.
catch:
- events: {}
- describe:
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
name: postgres-test
- podLogs:
selector: cnpg.io/cluster=postgres-test
tail: 50
steps:
- name: create-postgres
try:
- apply:
file: postgres.yaml
- name: wait-helmrelease-ready
try:
- assert:
timeout: 6m
resource:
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: postgres-test
status:
# JMESPath filter: condition order in the array does not matter
(conditions[?type == 'Ready']):
- status: "True"
- name: wait-init-job-complete
try:
- assert:
timeout: 2m
resource:
apiVersion: batch/v1
kind: Job
metadata:
name: postgres-test-init-job
status:
(conditions[?type == 'Complete']):
- status: "True"
- name: verify-services-and-endpoints
try:
# Replaces three `until kubectl get svc ... | grep -q 5432` loops
# TODO(chainsaw>0.2.15): the (ports[0].port) / (ports[*].port) projection form in
# these Service asserts is a v0.2.15 workaround for mis-evaluated number-literal
# filters (ports[?port==`5432`]); (ports[0]) also checks only the first port.
# Restore the filter form on a chainsaw bump. Same workaround lives in
# kafka/mariadb/harbor — see "Chainsaw v0.2.15 gotchas" in docs/agents/e2e-testing.md.
- assert:
resource:
apiVersion: v1
kind: Service
metadata:
name: postgres-test-r
spec:
(ports[0].port): 5432
- assert:
resource:
apiVersion: v1
kind: Service
metadata:
name: postgres-test-ro
spec:
(ports[0].port): 5432
- assert:
resource:
apiVersion: v1
kind: Service
metadata:
name: postgres-test-rw
spec:
(ports[0].port): 5432
# Replaces `until kubectl get endpoints ... | grep -q '[0-9]'` loops.
# The read-only endpoint is skipped, same as in the BATS test.
- assert:
timeout: 3m
resource:
apiVersion: v1
kind: Endpoints
metadata:
name: postgres-test-r
(subsets[0].addresses[0].ip != null): true
- assert:
timeout: 3m
resource:
apiVersion: v1
kind: Endpoints
metadata:
name: postgres-test-rw
(subsets[0].addresses[0].ip != null): true
# Functional probe — the BATS test never actually talks to the database.
- script:
timeout: 2m
content: |
set -eu
pod=$(kubectl -n "$NAMESPACE" get pod \
-l cnpg.io/cluster=postgres-test,cnpg.io/instanceRole=primary \
-o name | head -n1)
kubectl -n "$NAMESPACE" exec "$pod" -c postgres -- \
psql -U postgres -tAc 'SELECT 1'
check:
($error == null): true
(contains($stdout, '1')): true
# The init-job is operator-created (not applied by chainsaw), so it is
# not part of automatic cleanup — delete it explicitly, as BATS does.
finally:
- delete:
ref:
apiVersion: batch/v1
kind: Job
name: postgres-test-init-job
# The Postgres CR itself was applied by chainsaw and is deleted (and waited
# on) automatically during cleanup — no explicit teardown step needed.
---
# Full barman-cloud backup -> to-copy RestoreJob round-trip, driving the
# validated example scripts (examples/backups/postgres) as the harness so the
# test and the documented flow can never drift. run-all.sh runs Bucket ->
# source Postgres + sentinel row -> CNPG strategy + BackupClass -> ad-hoc
# BackupJob (waits Succeeded) -> empty target Postgres + to-copy RestoreJob
# (waits Succeeded) and asserts the sentinel round-tripped through S3 into the
# restored copy — the in-cluster witness that a barman-cloud plugin backup is
# both taken and restorable end to end.
#
# Runs in tenant-root, not the default tenant-test: the S3 client here is the
# barman-cloud sidecar INSIDE the Postgres Pod, and the e2e tenant is created
# isolated (hack/e2e-install-cozystack.bats) — its Cilium egress allowlist
# blocks a tenant-test Pod from reaching tenant-root's seaweedfs, while a
# tenant-root Pod reaches it over same-tenant egress. The harness targets the
# in-cluster endpoint (S3_ENDPOINT below) instead of the BucketInfo external
# ingress URL, which in CI is an unroutable placeholder, and trusts its
# self-signed cert by copying the seaweedfs CA secret (seaweedfs-ca-cert,
# auto-discovered from the cert-manager Certificate) into the per-app
# endpointCA Secret. (The bucket suite's mc checks lean on the same
# tenant-root seaweedfs, just from the test runner via port-forward.)
apiVersion: chainsaw.kyverno.io/v1alpha1
kind: Test
metadata:
name: postgres-2-backup-roundtrip
spec:
namespace: tenant-root
skipDelete: true
catch:
- events: {}
- describe:
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
name: postgres-pg-src
- describe:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
name: postgres-pg-src
- describe:
apiVersion: barmancloud.cnpg.io/v1
kind: ObjectStore
- describe:
apiVersion: backups.cozystack.io/v1alpha1
kind: BackupJob
- describe:
apiVersion: backups.cozystack.io/v1alpha1
kind: RestoreJob
# The barman-cloud backup/WAL work runs in the cnpg Cluster Pods' sidecar; a
# failed upload or TLS handshake leaves its error only in that Pod's log.
- podLogs:
selector: cnpg.io/cluster=postgres-pg-src
tail: 200
steps:
- name: run
try:
# The 25m budget is the whole documented flow: bucket provisioning, a
# 2-replica source Postgres, the plugin base backup to S3, an empty target
# app, and the purge + re-bootstrap restore (convention 6: justified
# in-line). run-all.sh fails fast on terminal BackupJob/RestoreJob phases
# and stalled HelmReleases rather than polling out the budget.
- script:
timeout: 25m
content: |
set -eu
cd ../../..
[ -x examples/backups/postgres/run-all.sh ] || { echo "postgres backup example scripts not found at examples/backups/postgres" >&2; exit 1; }
NAMESPACE="$NAMESPACE" S3_ENDPOINT="https://seaweedfs-s3.tenant-root:8333" \
examples/backups/postgres/run-all.sh
check:
($error == null): true
finally:
# Resumes the driver-suspended HelmReleases before deleting the RestoreJob
# so the apps are not stranded, then removes every resource the harness
# created (chainsaw did not apply them, so they are not part of automatic
# cleanup).
- description: backup-flow cleanup
script:
timeout: 8m
content: |
set -eu
cd ../../..
NAMESPACE="$NAMESPACE" examples/backups/postgres/cleanup.sh