Kubernetes acceptance with vind and External Secrets Operator
Run a disposable real Kubernetes cluster with vind (vCluster's Docker driver), Calico, the published Moco server/controller charts, and External Secrets Operator's webhook provider. The test downloads checksum-pinned tools and manifests, creates fresh credentials and a private CA, and never uses production credentials.
Run
Requirements: Linux/macOS amd64/arm64, a working local Docker daemon, Python 3, OpenSSL 3, Helm 3/4, and enough Docker memory for Kubernetes, Calico and the test workloads (8 GiB recommended).
make vind-test
The runner creates an independently named cluster, uses a temporary vCluster
configuration and kubeconfig, and deletes the cluster at the end, including
after failure. It does not switch the user's global Kubernetes context or
vCluster driver. Tool downloads are cached under ignored tools/bin/kubernetes/;
checksums and component versions are in
deploy/testing/versions.json.
The default mode tests published v0.0.3 artifacts. It verifies the immutable
release.json checksum, pulls both charts from GHCR by version, checks their
OCI digests and archive SHA-256 values, and validates the default image pins and
controller CRDs before creating the cluster. Both charts are installed from
those downloaded archives. Bootstrap uses /moco-ops from the released server
image; no application binary or image is built from the checkout. The chart image
defaults remain in use, and the suite verifies installed chart/app versions and
running Pod images against the release pins.
For controller development, make vind-source-test builds the bootstrap
operations and controller image from the checkout, imports the image into the
vind node, and uses the source charts. This optional mode requires Go and Docker
buildx. Select source in the workflow input, or use --source with the wrapper.
The report explicitly distinguishes published-release from source.
An SSH Docker context cannot bind the runner's local temporary files on the
daemon machine. Run the command on the Docker host in that case. A nested
LXC Docker host must provide the kernel facilities needed by kubelet and the
CNI. The development LXC/OrbStack environment exposes /proc/kmsg but denies
access to it, preventing kubelet from starting. This is a cluster bootstrap
failure, not a successful application test. Do not substitute fake kernel
devices or disable the network policies to obtain a passing report.
The vind-eso GitHub Actions workflow runs the same suite on an isolated
Ubuntu Docker runner. Dispatch it from the Actions tab or:
gh workflow run vind-eso.yml --repo araihu/moco -f mode=release
Scenarios
The acceptance report records each scenario independently:
- Install a real vind cluster, enforcing Calico CNI, ServiceMonitor CRD and ESO.
- Provision distinct data, local-backup, mirror and restore volumes; initialize
the mirror marker with the real
moco-opsimage. - Bootstrap credentials with
moco-ops init, deploy the production chart, and wait for TLS and verified mirrored-backup readiness. - Install the published controller chart and establish the three CRDs with namespace-scoped RBAC and private TLS access.
- Verify both installed chart/app versions and their running OCI image digests.
- Create
MocoTenant,MocoVaultandMocoAuthorizationdeclaratively; wait for tenant references, verify Ready conditions, reconcile the full authorization snapshot, and repeat apply without creating new resources. - Repair tenant/authorization drift, recover status after controller restart, validate immutable CRD identities and singleton authorization, and refresh repaired controller credentials.
- Deny other tenants/vaults, reader writes, unauthenticated reads and public administrative API requests.
- Apply
SecretStore/ExternalSecrettwice, fetch a URL-escaped logical path, decode a binary secret exactly once, and mount it in a consumer Pod. - Update the secret conditionally, reject a stale ETag, and observe both the Kubernetes Secret and the running consumer's volume update.
- Reject an invalid ESO bearer credential, retain the last valid Secret, and recover after repairing the credential.
- Reject an invalid CA and recover without disabling TLS verification.
- Renew the server certificate and verify the certificate actually served.
- Rotate reader and controller tokens and prove old tokens are rejected.
- Prove public/admin NetworkPolicy enforcement and protected TLS metrics.
- Observe automatic backups and compare local/mirror archive bytes.
- Rotate root keys, retain historical keys, advance the epoch and rewrap keys.
- Remove the mirror marker, observe failed backups/readiness, retain the consumer Secret, then restore the marker and recover.
- Replace the Moco Pod and prove SQLite data persists.
- Stop the writer, restore a pre-rotation mirrored snapshot into a different offline volume, restart and prove ESO recreates the application Secret.
- Delete the management CRs and verify retained Moco resources and ESO access.
The declarative webhook resources are in
deploy/testing/eso.yaml. They reference a
Secret containing only the scoped reader token and public CA, use $.value
and decodingStrategy: Base64, and retain the application Secret when the
upstream service fails. Never grant ESO the bootstrap administrator token.
ESO already URL-encodes remoteRef.key when expanding the URL: use
{{ .remoteRef.key }} directly and do not add urlquery. Otherwise slashes,
plus signs and spaces are encoded twice and Moco looks up a different path.
See the upstream ESO webhook documentation
and the pinned implementation.
Evidence and limits
bin/kubernetes-report.json records successful/failed scenarios, durations,
component pins, installed artifact identities, declarative resource IDs/conditions and limitations. CI uploads this report and sanitized Pod status
diagnostics for seven days. It never uploads kubeconfigs, credential files,
Secret objects, secret responses or test database archives.
These tests use static local ReadWriteOnce volumes on one disposable node. They do not certify a production CSI driver, ReadWriteOncePod fencing, independent off-host durability, actual homelab RPO/RTO, ingress-controller behavior or external alert delivery. The mirror is a separate test volume on the same Docker host. Validate those properties on the real homelab as well.
To keep a published-release test cluster for debugging, run:
python3 -B scripts/vind_test.py --keep
The printed private workspace holds the matching CLI config and kubeconfig.
Use that config with vcluster --config PATH/config.json delete CLUSTER --driver
docker afterward, then remove the private workspace. Treat a kept cluster and
its workspace as sensitive even though its credentials are disposable.
Management controller acceptance
The default acceptance path uses the published controller image and chart. Tenant,
vault and authorization setup goes through the CRDs before configuring ESO; only
secret values and out-of-band drift/failure fixtures use direct API writes.
artifactMode, releaseCommit, installedArtifacts and declarativeResources
in the report establish which binaries/charts and reconciliation outcomes were
tested. The optional source mode builds a temporary OCI archive and imports its
digest into the node. See the controller runbook.