Environment Lifecycle

State machine of a QA environment, the meta.json schema, and which pipeline step drives each transition.

States

states

In v2.6.0 there are six user-facing Jenkins jobs under QA-Environments/: Create-Environment, Deploy-Environment, Update-Environment, Manage-Environment, List-Environments, and Cleanup-Environments. Everything that used to be a Stop-Environment, Delete-Environment, or Restore-Environment job is now an ACTION on Manage-Environment (see Usage).

configured, deployed, stopped, failed, and unknown are the allowed values — see QAConfig.Status. meta.status = 'unknown' is used as a defensive fallback when the meta file is malformed.

meta.json schema

Each env owns a meta.json under /var/jenkins_home/qa-data/envs/<env>/. Typical content:

{
  "env_name":       "andromeda-1745",
  "database_type":  "mysql",
  "services":       ["mysql", "hazelcast", "ftacs", "ui-backend"],
  "image_versions": { "ftacs": "5.12.3", "ui-backend": "latest" },
  "runner":         "qa-runner-04",
  "status":         "deployed",
  "ttl":            "1w",
  "created_at":     "2026-04-22T09:14:51Z",
  "created_by":     "alice",
  "owners":         ["bob", "carol"],
  "last_action":    "deploy",
  "last_action_at": "2026-04-22T09:17:02Z"
}
Field Notes

env_name

Must match QAConfig.Validation.ENV_NAME_PATTERN. Immutable.

database_type

mysql / oracle / postgres. Changeable via Update-Environment or switchDatabaseStep.

services

Sorted list of enabled services. Must be a subset of the keys in resources/services/compose-<db>.yml.

image_versions

Per-service tag map; omitted entries fall back to the template default (${*_IMAGE:-…​}).

runner

Last agent the env was deployed on. Updated by deployEnvStep (and by restoreEnvStep when invoked internally).

status

One of QAConfig.Status.ALL.

ttl

One of QAConfig.TTL.ALL. cleanupEnvsStep enforces it.

created_at / created_by

Set once by createEnvStep. created_by is immutable and cannot be changed, not even by manage-owners.

owners

Mutable list of Jenkins logins granted the same RBAC rights as the creator (except they can also be removed). See RBAC & Ownership.

last_action / last_action_at

Written on every successful action for auditability.

All writes to meta.json go through utils.atomicWriteMeta(path, data) — temp file + rename — to avoid torn reads if a second job reads the file mid-write.

Who drives which transition

Job / step Transition Notes

createEnvStep

[*] → configured

Renders compose + .env.<db> + per-service .env + meta.json. Enforces MAX_ENVS_PER_USER for non-admins.

deployEnvStep

configured → deployed, stopped → deployed

Runs port-conflict check, docker compose up -d, updates meta.runner / meta.status.

updateEnvStep

stays in current state

Re-renders compose with new services / tags / vars. Strips Compose profiles from services it plans to start (commit cc8566b).

manageEnvStep with action=start / restart

stopped or failed → deployed

Thin wrapper over compose start.

manageEnvStep(action=stop)

deployed → stopped

compose stop. Volumes and config preserved.

manageEnvStep(action=delete)

any → deleted

compose down -v, removes config dir, optional pre-delete backup.

manageEnvStep(action=manage-owners)

no state change

Rewrites meta.owners[]. Skips the duplicate-audit code path used by the other actions (commit 867752a).

restoreEnvStep (helper, no standalone Jenkins job)

stopped or failed → deployed

Shared-library step reused by internal flows. End users reach the same outcome via Manage-Environment with ACTION=start (same runner) or Deploy-Environment (different runner).

rollbackEnvStep

rewinds to a snapshot

Uses createSnapshot / restoreFromSnapshot; state depends on the snapshotted status.

cleanupEnvsStep

expired → deleted

TTL sweep, RBAC-aware (non-admins only see their own envs; cron passes see all).

TTL and cleanup

cleanupEnvsStep is not scheduled by default — it must be triggered manually or wired to a cron trigger. When it runs it:

  1. Lists envs the actor can see (RBAC filtered).

  2. For each env with ttl != 'infinity', computes created_at + ttl against now().

  3. Calls the delete path for expired envs (with an optional grace window).

See Known Issues for the manual-trigger caveat.