Skip to content

One-command integration test runs

replicove run creates a fresh environment, waits for readiness, issues scoped guest credentials, runs your local test command, and waits for verified cleanup. It works from your terminal or any automation system that can run the CLI. There is no GitHub Action dependency.

replicove run -n replica-lab -f examples/testing/smoke.yaml

# Override the recipe's command. Arguments after -- go directly to the executable.
replicove run -n replica-lab -f examples/testing/smoke.yaml \
  --artifacts ./smoke-results -- go test ./integration/... -count=1

The command runs locally, in the current working directory, with your normal environment except for a private, temporary KUBECONFIG. Replicove does not edit your current context or default kubeconfig. The command must use that kubeconfig when accessing Kubernetes; explicitly overriding it in your test code overrides the runner's destination too. No shell is inserted automatically. Use an explicit shell executable only when your command needs shell syntax.

Prerequisites

Install Replicove and provision an administrator-approved destination namespace and ReplicaGrant. The selected grant must permit the source configuration, guest access role, credential duration, and requested lifetime. The caller needs the existing request and access permissions, exact credential-Secret reads, and runtime pod port-forward permission described in access. A test runner does not grant itself these permissions.

The runner creates a managed runtime. It does not adopt an existing ClusterReplica, ReplicaMirror, or an administrator's existing target. vCluster's one-runtime-per-host-namespace limitation still applies: use an available granted destination, or separate destinations for concurrent runs. An occupied destination can prevent readiness and exhaust the setup deadline.

Local recipe format

Recipes are versioned local CLI documents, not CRDs; do not apply them with kubectl. Unknown fields, duplicate fields and multiple documents are rejected. A recipe embeds exactly one replica (ClusterReplicaSpec) or mirror (ReplicaMirrorSpec). Metadata, status, finalizers and existing names cannot be imported.

apiVersion: replicove.nimeshbuilds.dev/v1alpha1
kind: TestRecipe
namePrefix: integration
replica:
  profile: vcluster-0.37.1-persistent
  ttl: 2h
  cleanupPolicy: DeleteOwned
  approval: Automatic
  grantRef: source-dev
  target:
    provider: helm
  replication:
    namespaces: [source-dev]
    namespaceMap:
      source-dev: integration
    secrets: None
    data: None
execution:
  command: [go, test, ./integration/..., -count=1]
  timeout: 10m
  role: viewer
  credentialSeconds: 900
lifecycle:
  readyTimeout: 20m
  cleanupTimeout: 15m
  keepOnFailure: false
Field Behavior
namePrefix Optional DNS label, at most 40 characters; defaults to test. A random run identity is appended.
destinations Optional explicit namespace/grant pairs from an administrator-managed pool. The CLI chooses a readable member with the fewest requests. Cannot be combined with an explicit --namespace.
replica / mirror Exactly one spec. Requires DeleteOwned, a supported pinned profile, a grant, and Automatic approval. Existing targets and secrets: Follow are rejected.
execution.command Argument array. May be omitted when a command follows CLI --. No implicit shell expansion.
execution.timeout Defaults to 10m; positive and at most 27m. Covers command execution, separately from readiness and cleanup.
execution.role viewer by default; deployer or admin must be explicitly selected and permitted by the grant.
execution.credentialSeconds Defaults to 900; allowed range 600..3600. Must cover the test timeout plus 30 seconds, and be within the grant. Issued credentials are checked again before execution.
lifecycle.readyTimeout Defaults to 20m; at most 2h. Includes provisioning, mirror lease acceptance, access issuance and optional chaos activation.
lifecycle.cleanupTimeout Defaults to 15m; at most 1h. Shared deadline for fault rollback, credential revocation, lease release and finalizer completion.
lifecycle.keepOnFailure Defaults to false. After an executed command fails, keep only this request until its original TTL. Provisioning failures and cancellation still trigger deletion.
chaos Optional bounded guest experiment, described below. Requires the chaos module and administrator delegation.

Choose a request TTL that exceeds readiness + execution + cleanup timeouts and also leaves at least ten minutes for credential issuance after the readiness allowance. TTL starts when Kubernetes creates the request, including provisioning. The runner never extends TTL to keep failed environments alive.

Storage-backed test environments

Use the mirror recipe example to create an owned mirror for one test run. The mirror module, qualified CSI snapshot/restore behavior, enforced NetworkPolicies and volume-data grants must already be configured.

The runner waits for an active generation, acquires a bounded mirror test lease, and waits for the operator to acknowledge it. It records the active replica UID, capture-run UID and capture timestamp before issuing access. Test recipes do not accept an initial hold, suspension or automatic reset interval. The runner watches the lease, active generation and plan revision while the command runs; a detected change stops the command. The lease is released during teardown.

Mirroring continues to mean per-volume crash consistency. A test recipe does not make CSI captures transaction-consistent across databases or multiple volumes. Administrator-authorized forced resets can bypass a mirror lease; the runner detects changed generation identity rather than silently switching the test to a new generation.

Run tests with chaos

Add a chaos section to either kind of recipe. Faults in one experiment are simultaneous. The duration must be 1..900 seconds, no longer than the execution timeout, and permitted by the grant. There may be at most eight faults. The runner waits for Active before launching the command and waits for rollback during teardown.

chaos:
  durationSeconds: 60
  faults:
    - kind: NetworkIsolation
      namespace: integration
    - kind: ScaleZero
      namespace: integration
      target:
        kind: Deployment
        name: orders

For named Pod/Deployment/StatefulSet targets, a local recipe may omit target.uid. The CLI resolves the exact named object using the scoped guest session, then submits an experiment with its current guest UID. The ReplicaExperiment CRD itself always requires a UID. A provided UID is used exactly; source/host UIDs must not be substituted for guest UIDs. Ownership and allowed-fault checks remain in the controller.

The same typed fault fields support PodDelete, ScaleZero, NetworkIsolation, CPUStress, MemoryStress and CustomJob. Custom Jobs are constrained by the administrator's image and resource policy; the runner does not accept arbitrary host manifests or privileged pod specifications. See the chaos guide for grant configuration, supported fault behavior and rollback limitations. The chaos recipe example demonstrates one network fault; replace its API smoke check with your application's actual resilience assertions.

Results, provenance and reproducibility

Runs that reach provisioning write report.json and junit.xml into a new private directory. Use --artifacts to choose the path; an existing directory or file is never overwritten. Files are mode 0600, and the created directory is mode 0700. JUnit contains three stages: provisioning, command, and cleanup. It does not parse or replace your test framework's individual test-case output.

The report records the normalized recipe hash, exact request and guest UIDs, plan revision and capture time, safe resource identity/provenance, runtime profile/chart checksum/Kubernetes version, optional mirror capture and chaos target references, test exit code, and independent setup/test/cleanup outcomes. It excludes the recipe body, command arguments, captured configuration, arbitrary API error messages, credentials, kubeconfig paths and test output. Command stdout/stderr stream directly to your terminal; your test program controls what it prints.

Reusing a recipe repeats the requested setup against a fresh host capture. It does not guarantee historical image contents, external-service state, database contents or identical live source resource versions. A recorded plan revision identifies that specific capture and includes its capture time, so even unchanged source configuration receives a new revision on a fresh capture. It cannot compare configuration equality across runs or import a snapshot. Use immutable image digests, retain the original recipe in your own source control, and use retained mirror reset revisions when you need captured data again. There is no automatic download or replay of the encrypted captured configuration.

Cleanup and failed tests

Normal teardown closes the tunnel and erases the private credential file, deletes any owned chaos experiment and waits for its rollback finalizer, deletes the scoped access request and waits for credential revocation, releases the mirror lease, then deletes the owned replica or mirror and waits for its finalizer-backed removal. Delete calls carry exact UID preconditions. If a name has been reused, the replacement is preserved and cleanup is reported as unverified.

Cancellation and execution timeouts still run cleanup under a separate bounded context. On macOS and Linux, cancellation terminates the command's process group, including ordinary child processes. Force-killing the CLI or losing the machine prevents local cleanup; the original server-side TTL remains the fallback. No runner can guarantee cleanup while the Kubernetes API or cleanup controller is permanently unavailable.

The process exits successfully only when setup and the command pass and cleanup is verified. A passing command with unfinished cleanup is still a failed run, with separate fields in the report. keepOnFailure: true retains a failed test request until its original TTL after rolling back chaos and revoking the test session. Use the printed request name and normal access commands to request fresh, authorized debugging access. Retained is explicit, not reported as completed cleanup.

If cleanup cannot be verified, inspect the retained request and its conditions using replicove status, replicove mirror status or kubectl. Do not remove finalizers to make the runner appear successful. The cleanup guide explains ownership checks and recovery.