GCP acceptance (local, destroy-on-exit)¶
Catalog ownership: certification-gcp cells in the capability matrix
plus evidence. Certified subset is GKE
Autopilot evidenced runtime cells. Standard and HA stay experimental.
gcap29 KEEP is closed (destroy emptied the prefix). It is not a
certified-row replacement for gcap28. Magento 2.4.8-p5 stays not-run
until a signed digest exists. Cell identity (runtime, Magento release, Cloud SQL
availability, Memorystore, OpenSearch, queue, digest) belongs in the evidence
file. GCP KEEP does not wait on AWS.
Status: current evidence exists for three 2.4.9 profiles, but the complete
release and service matrix remains open. On 2026-08-07, the preview profile
passed 12 cells with Cloud SQL MYSQL_8_4, database-backed messaging, and a
one-replica OpenSearch workload. Standard and HA Standard each passed 13 cells
with Cloud SQL MYSQL_8_4, RabbitMQ 4.3, pinned OpenSearch 3, and the real B2B
dump. Earlier 2.4.6-p15 standard and HA Standard runs also passed 13 cells with
the images and full dump of that date, but their MySQL engine is not an Adobe-listed
latest-patch database choice, and that Magento line is no longer supported. IDs are redacted in public docs.
Current proof lives in the evidence pack: Autopilot
Magento 2.4.9 preview is
gcap28;
GKE Standard Valkey 9.0 is
20260815;
HA known-content (pod/node/zone-loss simulation) is
gcha36.
Local re-runs write under .magelift/gcp-matrix/. Sibling path:
aws-acceptance.md.
A 2026-08-12 fail-closed infrastructure-only probe confirmed Memorystore
VALKEY_9_0 (ACTIVE, one shard, zero replicas, provider-reported
MULTI_ZONE). That is a cache version/topology boundary, not Magento
application certification.
Community alpha development loop¶
Use make development-loop-test for the gate and preflight regressions without
cloud credentials. make verify-community includes those tests, the scoped Go
suites and PHP checks. A successful dry-run records SKIP for the product gate;
passing offline artifact checks also leaves the actual image build NOT_RUN.
Neither result proves that Magento runs on GCP.
The product gate uses the ordered preview Autopilot application catalog. It
requires an explicitly selected acceptance project and an immutable image. Each
run gets separate checkpoints, evidence and private logs under
.magelift/development-loop/. A failed gate keeps its diagnostics and does not
stop the other gates. Tags and public publication require maintainer approval.
Live preflight checks the selected project, enabled APIs, effective ADC and a bounded set of create and cleanup permissions. Independent inventory then rejects an occupied or unreadable resource prefix before creation. These checks do not guarantee that every resource-level condition or quota will permit a run.
An existing auto-named state bucket or encryption key is never implicitly adopted for cleanup. External backends and key references must be explicit and remain external. Incomplete cleanup preserves recovery state and its passphrase and returns a failure. The normal product gate refuses retention, resume, infra-only runs and partial catalogs. Authorized debug work uses the harness separately and is not a passing product proof.
Offline harness shape¶
GCP shares the AWS acceptance control-flow shape (checkpoint/resume + evidence
append_row + destroy/force_clean_orphans/assert_clean on EXIT) without
spending credits in dry-run:
export MAGELIFT_ACCEPTANCE_DRY_RUN=1
bash tests/acceptance/gcp_harness_shape_test.sh
# or: ./providers/gcp/scripts/gcp-acceptance-local.sh
The combined account-free stack is make local-gates (Pulumi mocks, harness,
Floci AWS, floci-gcp).
| Artifact | Path |
|---|---|
| Cell catalog | scripts/acceptance/cells-gcp-preview.txt, cells-gcp-standard.txt, cells-gcp-high-availability.txt, or the automatic infra-only scripts/acceptance/cells-gcp-infra-only.txt |
| Checkpoint | .magelift/gcp-matrix/acceptance-checkpoint.json |
| Evidence | .magelift/gcp-matrix/matrix-results.md (gitignored local ledger); publish one current proof under evidence |
Evidence contract: certification requires harness append_row
PASS rows for SC1-SC5 cells. Hand-typed matrix rows are not certification
evidence. Resume kills mid-matrix at the first incomplete cell via
shared lib-checkpoint.sh (cell_done / record_cell).
Dry-run never sets created=1 and never calls Pulumi/gcloud up. Live create,
PSA soak timing (MAGELIFT_GCP_PSA_SOAK_SECS), and leftover proof are the paid
pass documented in the evidence pack.
Ordered cells (create-once, then updates)¶
Catalog order (teardown is EXIT, not a cell):
bootstrap:wif: Ensure WIF plus agcloudimpersonation token exchange. Ensure-only is refused. Impersonation checks IAM on the CI service account. The GitHub OIDC exchange runs in the acceptance shop workflow.composer:sm-write/composer:sm-read: Secret Manager Composer creds (gcp-secret-manager://); no nil-success.day2:secrets/day2:state/day2:logs/day2:exec/day2:healthmigrate:dump: after infrastructure create and before the candidate (D-03); kube dumpimport runner for private SQLdeploy:candidate: Magento CLI preflight, then migrate → cutover → health → record (kube.Steps)cost:estimatecutover:dns: afterapplicationURLexists (D-04);scripts/cutover-dns-cloudflare.sh
Create-once, before that catalog loop, runs magelift sign when a Cosign
identity-token source is configured, then magelift promote of
MAGELIFT_GCP_ACCEPTANCE_DIGEST with MAGELIFT_CERTIFICATE_IDENTITY
(or the MAGELIFT_GCP_CERTIFICATE_IDENTITY alias). Unsigned digests fail closed
at promote, not at Magento cutover. Rollback copies those signatures onto later
deploy rows.
Live EXIT contract: when created=1 and KEEP is false, cleanup runs destroy →
force_clean_orphans (PSA soak + peering teardown) → assert_clean. Dry-run
short-circuits soaks and never enters that path.
The current preview profile passed all 12 cells in its profile-scoped catalog.
It intentionally omits queue:health because database-backed messaging has no
broker. It still creates and checks OpenSearch, so it is not a no-search run.
See gcap28.
The 2.4.6-p15 standard shape passed all 13 cells with GKE Autopilot, one OpenSearch replica, and one RabbitMQ replica. Its HA shape passed all 13 cells with GKE Standard, three OpenSearch replicas, two RabbitMQ replicas, and node-level sysctl support. Those runs stay in the evidence pack. Adobe's 2.4.6-p15 latest-patch row does not list MySQL, and MageLift no longer accepts the line.
The current 2.4.9 standard and HA shapes passed all 13 cells with Cloud SQL
MYSQL_8_4, OpenSearch 3, RabbitMQ 4.3, and the B2B candidate. Those historical
runs used the former Valkey 8 shape. The implementation now selects
Memorystore VALKEY_9_0 (GA) for the 2.4.9 profile and permits
VALKEY_9_1 only as an explicit Preview profile. The corrected 2026-08-12
infrastructure-only probe proves the live VALKEY_9_0 API mapping, but did not
run the Magento image, dump, migration, or application cache checks; the full
application/cache intersection remains open. None of these profiles closes the
2.4.7-through-2.4.9 matrix.
Safety rules¶
- Set
MAGELIFT_GCP_ACCEPTANCE=1or the script refuses to run (unless dry-run). - Default mode is
preview(Pulumi plan only). upcreates the selected GKE runtime, Cloud SQL, and Memorystore Valkey. Costly and slow. Destroy always runs on EXIT unlessMAGELIFT_GCP_ACCEPTANCE_KEEP=true. High-availability defaults to GKE Standard because the three-node OpenSearch shape needs a node-level sysctl.- A failed cell also cleans up by default. Set
MAGELIFT_GCP_ACCEPTANCE_KEEP_ON_FAILURE=trueonly when you explicitly need to retain the paid stack for a resume. - The live harness assigns a six-hour disposable TTL by default. Override it with
MAGELIFT_ACCEPTANCE_TTL_SECONDS(maximum 24 hours); the watchdog forces the EXIT cleanup path at expiry and overridesKEEP=true. MAGELIFT_GCP_ACCEPTANCE_DIGESTmust be pullable for Magento cells (day2 logs/exec/health,deploy:candidate). The default placeholder digest fails ImagePull. For a managed-service/topology probe when no Magento image or real schema dump is available, setMAGELIFT_GCP_ACCEPTANCE_INFRA_ONLY=1and accept the automaticcells-gcp-infra-only.txtcatalog, or provide a custom catalog containing onlybootstrap:wif, Composer Secret Manager, state/secret inventory,search:health,queue:health, andcost:estimatecells. The harness rejects application cells in this mode before creating a state bucket or provider resource. Composer cells are accepted only whenMAGELIFT_GCP_COMPOSER_SECRET_IDis explicitly set to a run-owned$MAGELIFT_GCP_ACCEPTANCE_NAME-...secret; the preserved unmarkedmagelift-composer-authsecret is never a write target. Infra-only evidence is explicitly not Magento application certification.- Live defaults target Adobe Commerce 2.4.9, PHP 8.5, and Composer 2.10. The
candidate cell runs
bin/magento list --no-ansiin the ready web pod and requirescache:clean,cache:flush, andcache:statusbefore it starts a migration candidate. - The high-availability catalog includes
resilience:pod-loss. That bounded cell deletes one ready web pod, waits for a different ready replacement and the desired replica count, then reruns runtime health. It is pod-rescheduling evidence only; it does not claim node, zone, database, queue, cache, DR, fencing, or failback behavior. - Before live cells begin, the harness reads the Cloud SQL instance's actual
databaseVersionand compares it with the Magento release mapping. A mismatch fails the run before Magento cells spend more credits. - After destroy, the script asserts zero leftovers matching the acceptance name
prefix (
mlaccby default; override withMAGELIFT_GCP_ACCEPTANCE_NAME) for VPC, GKE, Cloud SQL, Memorystore, and secrets. - The disposable Pulumi state bucket is created only after the cleanup trap and all cleanup helpers are installed, so a preflight or state-bucket failure is still followed by bucket cleanup.
- Before ADC, API enablement, state-bucket creation, or any provider resource,
the harness checks local free space. The default
12 GiBthreshold protects the serial Go/Pulumi build from macOS temporary-volume exhaustion; override it only after measuring the local build footprint withMAGELIFT_GCP_ACCEPTANCE_MIN_FREE_MB. - Never interrupt mid-create/destroy.
- The harness builds its CLI under
GOMEMLIMITat 75% of available RAM (scripts/go-memlimit.sh). Do not pinGOMAXPROCSor-p. - When another agent works on AWS in the main checkout, run this from a separate
checkout and keep a unique acceptance prefix so resource names and
/tmpworkdirs do not collide.
Warm matrix sessions¶
The live catalog already creates one stack and updates it between cells. It does not destroy GKE, Cloud SQL, or Memorystore between cells. The expensive part is the first create and the provider teardown, so related application runs can use one retained stack:
export MAGELIFT_GCP_ACCEPTANCE=1
export MAGELIFT_GCP_PROJECT=YOUR_DISPOSABLE_PROJECT
export MAGELIFT_GCP_REGION=europe-west1
export MAGELIFT_GCP_ACCEPTANCE_PROFILE=standard
export MAGELIFT_GCP_ACCEPTANCE_NAME=warm249
export MAGELIFT_GCP_ACCEPTANCE_DIR=/tmp/magelift-gcp-warm249
export MAGELIFT_GCP_ACCEPTANCE_KEEP=true
export MAGELIFT_CERTIFICATE_IDENTITY='…' # Sigstore subject that signed DIGEST
# export MAGELIFT_CERTIFICATE_OIDC_ISSUER=https://accounts.google.com
# MAGELIFT_GCP_CERTIFICATE_IDENTITY remains an alias.
# Optional non-interactive sign (JSON argv; stdout is a JWT, never logged):
# export MAGELIFT_COSIGN_IDENTITY_TOKEN_ARGV='["gcloud","auth","print-identity-token","--audiences=sigstore","--include-email","--impersonate-service-account","sa@project.iam.gserviceaccount.com"]'
# Run one release and catalog. Change the release, digest, seed dump, or cell
# catalog for the next compatible run while keeping NAME and DIR unchanged.
./providers/gcp/scripts/gcp-acceptance-local.sh up
# After the last warm run, unset KEEP and resume the completed checkpoint. This
# performs the final destroy, orphan scan, and resource assertion.
export MAGELIFT_GCP_ACCEPTANCE_KEEP=false
export MAGELIFT_GCP_ACCEPTANCE_RESUME=1
./providers/gcp/scripts/gcp-acceptance-local.sh up
The checkpoint now includes a configuration fingerprint. A changed release,
image digest, runtime, or cell catalog starts a fresh cell set and retains the
old checkpoint as a local .stale-*.json file. Keep the same DIR so the
temporary Pulumi passphrase remains available for the retained state.
Warm reuse is appropriate for releases that keep the same database major and topology, such as the 2.4.7 through 2.4.9 Cloud SQL MySQL 8.4 runs. Use a cold stack for a database engine or major-version change, Autopilot versus Standard, preview versus high-availability, a provider change, or a managed-service replacement. Those tests verify provisioning and migration behavior that a warm update cannot prove.
The harness preserves Pulumi's dependency order during the first destroy pass:
Cloud SQL users and databases are removed before the instance, and the GKE
cluster stays behind the Kubernetes LoadBalancer Service. If that pass
returns a provider error, force_clean_orphans then starts exact producer
deletes and waits for direct inventories before removing the network. This is
slower than racing deletes, but it avoids orphaning child resources or making
the provider reject a valid destroy.
Invoke¶
# From this repository (use a separate checkout only if you need parallel AWS work)
cd /path/to/magelift
# Preview graph (default)
MAGELIFT_GCP_ACCEPTANCE=1 ./providers/gcp/scripts/gcp-acceptance-local.sh preview
# Short-lived real stack + live_cell_loop (destroy on EXIT)
MAGELIFT_GCP_ACCEPTANCE=1 \
MAGELIFT_GCP_ACCEPTANCE_DIGEST='ghcr.io/magelift/magento@sha256:…' \
./providers/gcp/scripts/gcp-acceptance-local.sh up
Resume after a mid-matrix kill (stack kept with KEEP):
MAGELIFT_GCP_ACCEPTANCE=1 \
MAGELIFT_GCP_ACCEPTANCE_KEEP=true \
MAGELIFT_GCP_ACCEPTANCE_RESUME=1 \
MAGELIFT_GCP_ACCEPTANCE_DIGEST='ghcr.io/…@sha256:…' \
./providers/gcp/scripts/gcp-acceptance-local.sh up
Optional env:
| Variable | Default | Purpose |
|---|---|---|
MAGELIFT_GCP_PROJECT |
required (live); dry-run uses example-gcp-project |
Your disposable GCP project; never commit real IDs |
MAGELIFT_GCP_REGION |
europe-west1 |
Region |
MAGELIFT_GCP_ACCEPTANCE_NAME |
mlacc |
Magento project name / resource prefix |
MAGELIFT_GCP_ACCEPTANCE_RUNTIME |
profile default | gke-autopilot or gke-standard; HA defaults to Standard when unset, and fails closed if a leftover Autopilot value is still set |
MAGELIFT_GCP_ACCEPTANCE_DIGEST |
placeholder digest | Must be pullable for Magento cells |
MAGELIFT_CERTIFICATE_IDENTITY |
required for Magento up |
Sigstore certificate identity; MAGELIFT_GCP_CERTIFICATE_IDENTITY is an alias |
MAGELIFT_CERTIFICATE_OIDC_ISSUER |
https://token.actions.githubusercontent.com |
Sigstore OIDC issuer; use https://accounts.google.com for Google SA tokens |
MAGELIFT_COSIGN_IDENTITY_TOKEN_ARGV |
unset | JSON argv that prints a JWT for magelift sign; file/env alternatives exist |
MAGELIFT_GCP_ACCEPTANCE_INFRA_ONLY |
0 |
1/true permits only the fail-closed managed-service/topology catalog; it does not certify Magento |
MAGELIFT_GCP_ACCEPTANCE_VERSION |
2.4.9 |
Adobe Commerce version sent to the runtime contract |
MAGELIFT_GCP_ACCEPTANCE_PHP |
8.5 |
PHP version sent to the build contract |
MAGELIFT_GCP_ACCEPTANCE_COMPOSER_VERSION |
2.10 |
Composer version sent to the build contract |
MAGELIFT_GCP_ACCEPTANCE_ENABLE_CLOUD_ARMOR |
false |
Create the GCP Cloud Armor policy; enable only for a quota-enabled WAF cell |
MAGELIFT_GCP_ACCEPTANCE_MEMORY_REQUEST |
2Gi |
Memory request for PHP-FPM, cron, queue, and migration workloads; lower values are an explicit under-sizing choice |
MAGELIFT_GCP_ACCEPTANCE_MIN_FREE_MB |
12288 |
Minimum local free space checked before ADC/API setup and the provider-specific build |
MAGELIFT_GCP_ACCEPTANCE_DIR |
/tmp/magelift-gcp-wt |
Work dir + logs |
MAGELIFT_GCP_ACCEPTANCE_RESUME |
unset | Skip create-once when stack already up |
MAGELIFT_GCP_EXEC_RETRY_ATTEMPTS |
10 |
Retries for the specific GKE No agent available exec error |
MAGELIFT_GCP_EXEC_RETRY_INTERVAL_SECS |
30 |
Delay between those exec retries |
MAGELIFT_DUMPIMPORT_RUNNER |
unset | Set kube for private Cloud SQL dump cell |
GOOGLE_APPLICATION_CREDENTIALS |
auto from gcloud login | Preferred; refreshable ADC |
GOOGLE_OAUTH_ACCESS_TOKEN |
avoided when ADC exists | Static token; expires mid-Up |
Native observability routing budget¶
The disposable native Google Cloud Logging/Monitoring cell is run with
providers/gcp/scripts/gcp-observability-acceptance-local.sh. Its default
MAGELIFT_GCP_OBSERVABILITY_VERIFY_BUDGET=4m is intentionally a cheap
configuration-and-delivery probe. A new sink can take substantially longer to
show matching entries in its destination, so an operator who needs the custom
bucket routing claim may set an explicit duration such as 1h; the command
accepts values in milliseconds, seconds, minutes, or hours and caps the budget
at two hours. During that wait it republishes the same marker-scoped log and
metric probes through the provider adapter; this creates no additional
dashboard, alert, bucket, sink, or metric-descriptor resources. The longer
budget also extends the lifecycle context so cleanup still has a five-minute
grace period. A four-minute timeout must be recorded as
long-latency/inconclusive, never as a passing retention-delivery result.
GKE collector delivery¶
The GKE Contrib collector cell runs through
providers/gcp/scripts/gcp-collector-acceptance-local.sh and verifies logs, metrics, and
traces through a separate New Relic query credential. The default mode uses an
existing cluster and never creates one. The live command requires
MAGELIFT_GCP_COLLECTOR_ACCEPTANCE=1.
For a disposable one-node GKE Standard cell, set the create gate and runtime explicitly. The harness derives a cluster name when one is not supplied, applies exact ownership labels, and deletes only that cluster after the collector lifecycle finishes:
MAGELIFT_GCP_COLLECTOR_ACCEPTANCE=1 \
MAGELIFT_GCP_COLLECTOR_CREATE_CLUSTER=1 \
MAGELIFT_GCP_COLLECTOR_RUNTIME=gke-standard \
MAGELIFT_GCP_COLLECTOR_CLUSTER_LOCATION=europe-west1-b \
MAGELIFT_GCP_COLLECTOR_IMAGE_DIGEST=docker.io/otel/opentelemetry-collector-contrib@sha256:YOUR_VERIFIED_DIGEST \
MAGELIFT_NEWRELIC_ACCOUNT_ID=YOUR_ACCOUNT_ID \
MAGELIFT_NEWRELIC_OTLP_ENDPOINT=https://otlp.eu01.nr-data.net \
MAGELIFT_NEWRELIC_NERDGRAPH_ENDPOINT=https://api.eu.newrelic.com/graphql \
make gcp-collector-acceptance-local
Use MAGELIFT_ACCEPTANCE_DRY_RUN=1 to inspect the resolved name and location
without provider mutation. GKE Standard creation uses one e2-medium node in
europe-west1-b by default, the regular release channel, the default VPC and
subnetwork, and a bounded exact-name delete poll. Autopilot and existing-cluster
cells use the same command with MAGELIFT_GCP_COLLECTOR_CREATE_CLUSTER=0.
Logs: /tmp/magelift-gcp-wt/logs/*.log. Grep those files; do not rely on
shell scrollback.
The live script builds cmd/magelift (the shipped CLI). Set
MAGELIFT_GCP_ACCEPTANCE_BIN to an already built binary to avoid rebuilding
during a resume.
Auth note: long Ups must use Application Default Credentials (refreshable).
A static GOOGLE_OAUTH_ACCESS_TOKEN expires (~40-60m) and fails GKE/Memorystore
operation polls with ACCESS_TOKEN_TYPE_UNSUPPORTED. The script materializes ADC
from ~/.config/gcloud/credentials.db when ADC is missing.
Force cleanup after a failed destroy¶
Cloud SQL can keep a Service Networking allocation for several minutes after the
instance is gone (FLOW_SN_DC_RESOURCE_PREVENTING_DELETE_CONNECTION). The EXIT
trap waits for GKE/SQL/Memorystore/SCP producers to clear, soaks
(MAGELIFT_GCP_PSA_SOAK_SECS, default 180), then removes the peering via Compute
networks.removePeering (and async vpc-peerings delete), the PSA address, and
the VPC. If assert_clean still fails, wait and retry:
# Prefer Compute removePeering when services vpc-peerings delete races soft-delete
# Substitute YOUR_PROJECT and PREFIX from the failed run.
TOKEN=$(gcloud auth print-access-token)
curl -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
"https://compute.googleapis.com/compute/v1/projects/YOUR_PROJECT/global/networks/PREFIX-preview-net/removePeering" \
-d '{"name":"servicenetworking-googleapis-com"}'
gcloud compute addresses delete PREFIX-preview-sql-psa --global --project=YOUR_PROJECT
gcloud compute networks delete PREFIX-preview-net --project=YOUR_PROJECT
Credentials¶
The harness checks jq (and Mike Farah yq v4 where YAML is patched) before
it provisions GKE, Cloud SQL, or Memorystore. See
acceptance command dependencies for install
guidance and the offline preflight test.
gcloudmust be authenticated to the target project.- Pulumi needs a backend. The harness ignores an ambient
PULUMI_BACKEND_URLso a stale login cannot select a deleted bucket; setMAGELIFT_GCP_ACCEPTANCE_BACKEND_URLonly when intentionally using an externally managed backend. Otherwise it creates and cleans its disposable GCS bucket. Ephemeral agent accounts from Automation API must be claimed by a human if used. - Prefer Application Default Credentials; the script falls back to
GOOGLE_OAUTH_ACCESS_TOKENfromgcloud auth print-access-token. - Cloudflare DNS cell needs
CLOUDFLARE_API_TOKEN/CF_API_TOKENwith Zone.DNS Edit (Wrangler OAuth is insufficient).
Known gaps (post-certify)¶
- Memorystore Valkey needs a regional Service Connection Policy
(
serviceClass=gcp-memorystore). Created by the GCP cache component. - Non-preview presets remain spend-gated (not free-tier certified).
- This repository does not run a GitHub WIF workflow. The hosted exchange is the acceptance shop workflow. See gcp-experimental.md. Do not commit SA keys.
- Live dump cell requires
MAGELIFT_DUMPIMPORT_RUNNER=kube(+ mysql client pod when the Magento image lacksmysql); evidenced on the Autopilot certified path. - A 2026-08-04 disposable run passed WIF, Composer Secret Manager, secrets,
state, and logs, then failed at GKE
No agent availableduringday2:exec. - The 2026-08-05 catalog run completed CLI preflight, seed import, candidate migration, health, and cleanup. The WIF row used the operator-attested Act proof flag, not a hosted Actions run.
- The 2026-08-07 2.4.9 MySQL 8.4 standard retry reached the real B2B dump but
failed at
migrate:dumpwith MySQL error 1419. The adapter now enableslog_bin_trust_function_creators.
Current proof¶
| Item | Value |
|---|---|
| Pack | evidence/README.md |
| Certified Autopilot Magento | gcap28 |
| Experimental Standard | 20260815 |
| Experimental HA Standard | gcha36 |