Skip to content
PtahOperator

Close the drift

Someone changes the database by hand. The operator notices and puts it back.

What it shows. What drift detection reads, and what it does about what it finds.

A replay of a verified run, not a live cluster. 6 checks held while it was recorded. Play types it out in the frame below; the transcript is the same session, already there.

sh · drift
# Converged, and the operator says so with a reason rather than a timestamp.$ kubectl -n "$NAMESPACE" get ptahschema storefront -o json \  | jq -r '.status.conditions[] | select(.type == "DriftDetected" or .type == "InSync") | "\(.type)\t\(.status)\t\(.reason)"'DriftDetected	False	ScopedConvergedInSync	True	ScopedConverged # Now change the database behind its back, the way an incident does.$ kubectl -n "$NAMESPACE" exec deploy/demo-psql -- psql -c "ALTER TABLE customers DROP COLUMN signed_up_at"ALTER TABLE # The column is gone. The declaration still asks for it.$ kubectl -n "$NAMESPACE" exec deploy/demo-psql -- psql -c "\d customers"             Table "public.customers" Column |  Type  | Collation | Nullable | Default--------+--------+-----------+----------+--------- id     | bigint |           | not null | email  | text   |           | not null |Indexes:    "customers_pkey" PRIMARY KEY, btree (id)Referenced by:    TABLE "orders" CONSTRAINT "fk_orders_customer_id" FOREIGN KEY (customer_id) REFERENCES customers(id) # Nobody is asked. The operator observes on its interval and closes it.$ kubectl -n "$NAMESPACE" exec deploy/demo-psql -- psql -c "\d customers"                         Table "public.customers"    Column    |           Type           | Collation | Nullable | Default--------------+--------------------------+-----------+----------+--------- id           | bigint                   |           | not null | email        | text                     |           | not null | signed_up_at | timestamp with time zone |           |          |Indexes:    "customers_pkey" PRIMARY KEY, btree (id)Referenced by:    TABLE "orders" CONSTRAINT "fk_orders_customer_id" FOREIGN KEY (customer_id) REFERENCES customers(id) # It closed the drift with a plan, the same way it does everything else.$ kubectl -n "$NAMESPACE" get ptahschemaplanNAME                                 SCHEMA       FINGERPRINT                                                               STATEMENTS   DESTRUCTIVE   AGEptah-plan-5b18ea4a0dba32b2f833e785   storefront   sha256:5b18ea4a0dba32b2f833e785a430f249a6cc92d18511d53da5831c9045b46f90   3            false         2m5sptah-plan-ea24297faf3b1dd3895f4e14   storefront   sha256:ea24297faf3b1dd3895f4e140a2a1675e4eb272ff27be536042561abf34deda0   1            false         23s

What was checked

Every step states what has to hold before its output may be published. A condition is read as its type, status and reason on the live object, never as a phrase in a message.

StepClaimHeld
1exits 0, prints "ScopedConverged"yes
2exits 0, prints "ALTER TABLE"yes
3exits 0, never prints "signed_up_at"yes
4exits 0, prints "signed_up_at"yes
5ptahschema/storefront: DriftDetected=False (ScopedConverged)yes
5exits 0, prints "storefront"yes

What it ran against

  • Kubernetes1.37.0
  • Operator8c7fe187707f
  • Ptahv0.6.0
  • Executorsha256:91ec0a6fa27f
  • Recorded2026-09-17
  • Scenarios at8c7fe187707f

What the commands read

A session is repeated, not admired, so every name in it is one your own environment can carry. There is nothing else: no helper of ours stands between a command and the cluster, and the recorder refuses a step that reads a name this list does not hold. The commands arekubectl, ptah and kubectl ptah, each of which you install once; the last one is the operator’s own read-only client.

  • KUBECONFIGthe cluster the commands run against
  • NAMESPACEthe namespace the schema and its database credentials live in
  • OPERATOR_NAMESPACEwhere the chart installed the operator
  • CONTROLLERthe controller Deployment’s name, from that release
  • REGISTRY_IN_CLUSTERthe registry address a Pod in the cluster resolves
  • PTAH_OCI_REGISTRYthe registry address the push goes to
  • PTAH_OCI_USERNAMEthe account that push uses
  • PTAH_OCI_PASSWORDits password, which no command prints

A session also uses demo/bin/lab in two places, and both are the lab handing over what it generated rather than doing the demonstration’s work:lab credentials prints the three registry values above, and lab manifestfills the published template with values the step states. Rundemo/acceptance/reproduce.sh to watch the same scenario repeated from a directory where neither exists, by an account that may not create a Job.

The scenario is demo/scenarios/drift.yaml, and make demo replays the whole set against a lab of your own. All recorded runs.