@caelo-cms/provisioning 0.11.9 → 0.11.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter.d.ts +6 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/admin-domain.d.ts +10 -1
- package/dist/admin-domain.d.ts.map +1 -1
- package/dist/admin-domain.js +13 -3
- package/dist/admin-domain.js.map +1 -1
- package/dist/adopt-existing.d.ts +12 -3
- package/dist/adopt-existing.d.ts.map +1 -1
- package/dist/adopt-existing.js +143 -0
- package/dist/adopt-existing.js.map +1 -1
- package/dist/agent/command.d.ts +23 -0
- package/dist/agent/command.d.ts.map +1 -1
- package/dist/agent/command.js +32 -9
- package/dist/agent/command.js.map +1 -1
- package/dist/agent/run.d.ts +2 -0
- package/dist/agent/run.d.ts.map +1 -1
- package/dist/agent/run.js +5 -1
- package/dist/agent/run.js.map +1 -1
- package/dist/agent-db.js +69 -10
- package/dist/aws-task-roles.d.ts +4 -2
- package/dist/aws-task-roles.d.ts.map +1 -1
- package/dist/aws-task-roles.js +4 -1
- package/dist/aws-task-roles.js.map +1 -1
- package/dist/caddy.d.ts +21 -4
- package/dist/caddy.d.ts.map +1 -1
- package/dist/caddy.js +27 -3
- package/dist/caddy.js.map +1 -1
- package/dist/cli-routing.js +1 -1
- package/dist/cli-routing.js.map +1 -1
- package/dist/cli.js +68 -7
- package/dist/cli.js.map +1 -1
- package/dist/cloudsql-tls.d.ts +93 -0
- package/dist/cloudsql-tls.d.ts.map +1 -0
- package/dist/cloudsql-tls.js +295 -0
- package/dist/cloudsql-tls.js.map +1 -0
- package/dist/compose.d.ts +43 -6
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +148 -23
- package/dist/compose.js.map +1 -1
- package/dist/converge-checkpoint.d.ts +1 -0
- package/dist/converge-checkpoint.d.ts.map +1 -1
- package/dist/converge-checkpoint.js +8 -1
- package/dist/converge-checkpoint.js.map +1 -1
- package/dist/converge-db-trust.d.ts +44 -0
- package/dist/converge-db-trust.d.ts.map +1 -0
- package/dist/converge-db-trust.js +158 -0
- package/dist/converge-db-trust.js.map +1 -0
- package/dist/converge-job-gcp.d.ts +5 -2
- package/dist/converge-job-gcp.d.ts.map +1 -1
- package/dist/converge-job-gcp.js +11 -13
- package/dist/converge-job-gcp.js.map +1 -1
- package/dist/converge-job-preflight.d.ts.map +1 -1
- package/dist/converge-job-preflight.js +8 -0
- package/dist/converge-job-preflight.js.map +1 -1
- package/dist/converge-job.d.ts +17 -0
- package/dist/converge-job.d.ts.map +1 -1
- package/dist/converge-job.js +12 -0
- package/dist/converge-job.js.map +1 -1
- package/dist/deployed-release.d.ts +4 -1
- package/dist/deployed-release.d.ts.map +1 -1
- package/dist/deployed-release.js +18 -8
- package/dist/deployed-release.js.map +1 -1
- package/dist/gcp-names.d.ts +34 -0
- package/dist/gcp-names.d.ts.map +1 -1
- package/dist/gcp-names.js +45 -0
- package/dist/gcp-names.js.map +1 -1
- package/dist/gcp-url-map.d.ts.map +1 -1
- package/dist/gcp-url-map.js +6 -2
- package/dist/gcp-url-map.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/infra-contract.d.ts +1 -1
- package/dist/infra-contract.d.ts.map +1 -1
- package/dist/infra-contract.js +45 -1
- package/dist/infra-contract.js.map +1 -1
- package/dist/install-job.d.ts +4 -2
- package/dist/install-job.d.ts.map +1 -1
- package/dist/install-job.js +4 -1
- package/dist/install-job.js.map +1 -1
- package/dist/install-state.d.ts +13 -0
- package/dist/install-state.d.ts.map +1 -1
- package/dist/install-state.js +17 -4
- package/dist/install-state.js.map +1 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +20 -8
- package/dist/lifecycle.js.map +1 -1
- package/dist/mcp-auth-proxy-setup.d.ts +48 -0
- package/dist/mcp-auth-proxy-setup.d.ts.map +1 -0
- package/dist/mcp-auth-proxy-setup.js +280 -0
- package/dist/mcp-auth-proxy-setup.js.map +1 -0
- package/dist/migration-runner.d.ts.map +1 -1
- package/dist/migration-runner.js +8 -3
- package/dist/migration-runner.js.map +1 -1
- package/dist/migration-targets.d.ts +11 -0
- package/dist/migration-targets.d.ts.map +1 -0
- package/dist/migration-targets.js +12 -0
- package/dist/migration-targets.js.map +1 -0
- package/dist/operator-access.d.ts +10 -0
- package/dist/operator-access.d.ts.map +1 -1
- package/dist/operator-access.js +5 -0
- package/dist/operator-access.js.map +1 -1
- package/dist/pool-budget-stack.d.ts +13 -1
- package/dist/pool-budget-stack.d.ts.map +1 -1
- package/dist/pool-budget-stack.js +11 -1
- package/dist/pool-budget-stack.js.map +1 -1
- package/dist/public-role-isolation.d.ts +7 -3
- package/dist/public-role-isolation.d.ts.map +1 -1
- package/dist/public-role-isolation.js +25 -13
- package/dist/public-role-isolation.js.map +1 -1
- package/dist/release-contract.d.ts.map +1 -1
- package/dist/release-contract.js +3 -0
- package/dist/release-contract.js.map +1 -1
- package/dist/release-manifest.d.ts +25 -1
- package/dist/release-manifest.d.ts.map +1 -1
- package/dist/release-manifest.js +36 -3
- package/dist/release-manifest.js.map +1 -1
- package/dist/remote-mcp.d.ts +55 -18
- package/dist/remote-mcp.d.ts.map +1 -1
- package/dist/remote-mcp.js +307 -77
- package/dist/remote-mcp.js.map +1 -1
- package/dist/runtime-secrets.d.ts +11 -0
- package/dist/runtime-secrets.d.ts.map +1 -1
- package/dist/runtime-secrets.js +40 -25
- package/dist/runtime-secrets.js.map +1 -1
- package/dist/scheduler-setup.d.ts +75 -0
- package/dist/scheduler-setup.d.ts.map +1 -0
- package/dist/scheduler-setup.js +331 -0
- package/dist/scheduler-setup.js.map +1 -0
- package/dist/seal-key-rotation.d.ts +7 -7
- package/dist/seal-key-rotation.d.ts.map +1 -1
- package/dist/seal-key-rotation.js +22 -22
- package/dist/seal-key-rotation.js.map +1 -1
- package/dist/self-hosted-db-tls.d.ts +37 -0
- package/dist/self-hosted-db-tls.d.ts.map +1 -0
- package/dist/self-hosted-db-tls.js +220 -0
- package/dist/self-hosted-db-tls.js.map +1 -0
- package/dist/self-hosted-release.d.ts +3 -1
- package/dist/self-hosted-release.d.ts.map +1 -1
- package/dist/self-hosted-release.js +33 -9
- package/dist/self-hosted-release.js.map +1 -1
- package/dist/self-hosted-roles.d.ts +43 -2
- package/dist/self-hosted-roles.d.ts.map +1 -1
- package/dist/self-hosted-roles.js +87 -1
- package/dist/self-hosted-roles.js.map +1 -1
- package/dist/self-hosted-scheduler-interval.d.ts +21 -0
- package/dist/self-hosted-scheduler-interval.d.ts.map +1 -0
- package/dist/self-hosted-scheduler-interval.js +68 -0
- package/dist/self-hosted-scheduler-interval.js.map +1 -0
- package/dist/self-hosted-start.d.ts +0 -21
- package/dist/self-hosted-start.d.ts.map +1 -1
- package/dist/self-hosted-start.js +39 -1
- package/dist/self-hosted-start.js.map +1 -1
- package/dist/self-hosted-upgrade.d.ts +19 -7
- package/dist/self-hosted-upgrade.d.ts.map +1 -1
- package/dist/self-hosted-upgrade.js +78 -25
- package/dist/self-hosted-upgrade.js.map +1 -1
- package/dist/stack-contract.d.ts +234 -28
- package/dist/stack-contract.d.ts.map +1 -1
- package/dist/stack-contract.js +397 -39
- package/dist/stack-contract.js.map +1 -1
- package/dist/stack-converge.d.ts +31 -0
- package/dist/stack-converge.d.ts.map +1 -1
- package/dist/stack-converge.js +71 -30
- package/dist/stack-converge.js.map +1 -1
- package/dist/staging-database.d.ts +22 -0
- package/dist/staging-database.d.ts.map +1 -0
- package/dist/staging-database.js +117 -0
- package/dist/staging-database.js.map +1 -0
- package/dist/upgrade/app-roll.d.ts +25 -4
- package/dist/upgrade/app-roll.d.ts.map +1 -1
- package/dist/upgrade/app-roll.js +63 -17
- package/dist/upgrade/app-roll.js.map +1 -1
- package/dist/upgrade/converge.d.ts +5 -0
- package/dist/upgrade/converge.d.ts.map +1 -1
- package/dist/upgrade/converge.js +207 -20
- package/dist/upgrade/converge.js.map +1 -1
- package/dist/upgrade/health.d.ts +31 -7
- package/dist/upgrade/health.d.ts.map +1 -1
- package/dist/upgrade/health.js +41 -17
- package/dist/upgrade/health.js.map +1 -1
- package/dist/upgrade/image-forms.d.ts +1 -1
- package/dist/upgrade/image-forms.d.ts.map +1 -1
- package/dist/upgrade/image-forms.js +24 -7
- package/dist/upgrade/image-forms.js.map +1 -1
- package/dist/upgrade/release-images.d.ts +12 -3
- package/dist/upgrade/release-images.d.ts.map +1 -1
- package/dist/upgrade/release-images.js +13 -4
- package/dist/upgrade/release-images.js.map +1 -1
- package/dist/upgrade/upgrade.d.ts +19 -3
- package/dist/upgrade/upgrade.d.ts.map +1 -1
- package/dist/upgrade/upgrade.js +37 -2
- package/dist/upgrade/upgrade.js.map +1 -1
- package/dist/upgrader-gcp.d.ts +13 -0
- package/dist/upgrader-gcp.d.ts.map +1 -1
- package/dist/upgrader-gcp.js +50 -1
- package/dist/upgrader-gcp.js.map +1 -1
- package/dist/upgrader-iam-preflight.d.ts.map +1 -1
- package/dist/upgrader-iam-preflight.js +6 -1
- package/dist/upgrader-iam-preflight.js.map +1 -1
- package/dist/upgrader-job.d.ts +1 -1
- package/dist/upgrader-job.d.ts.map +1 -1
- package/dist/upgrader-job.js +20 -14
- package/dist/upgrader-job.js.map +1 -1
- package/dist/upgrader-records.d.ts +1 -1
- package/dist/upgrader-records.d.ts.map +1 -1
- package/dist/upgrader-records.js +26 -7
- package/dist/upgrader-records.js.map +1 -1
- package/dist/upgrader-setup.d.ts.map +1 -1
- package/dist/upgrader-setup.js +10 -5
- package/dist/upgrader-setup.js.map +1 -1
- package/dist/wizard.d.ts +7 -0
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +11 -0
- package/dist/wizard.js.map +1 -1
- package/dist/wizards/gcp-cost.d.ts +31 -0
- package/dist/wizards/gcp-cost.d.ts.map +1 -1
- package/dist/wizards/gcp-cost.js +103 -18
- package/dist/wizards/gcp-cost.js.map +1 -1
- package/dist/wizards/gcp.d.ts.map +1 -1
- package/dist/wizards/gcp.js +129 -27
- package/dist/wizards/gcp.js.map +1 -1
- package/package.json +2 -2
- package/src/adapter.ts +6 -0
- package/src/admin-domain.test.ts +9 -8
- package/src/admin-domain.ts +13 -3
- package/src/admin-memory.test.ts +2 -1
- package/src/adopt-existing.test.ts +85 -0
- package/src/adopt-existing.ts +141 -3
- package/src/agent/command.test.ts +49 -1
- package/src/agent/command.ts +39 -9
- package/src/agent/run.test.ts +19 -0
- package/src/agent/run.ts +11 -2
- package/src/aws-task-roles.ts +5 -2
- package/src/caddy-redirects.docker.test.ts +2 -0
- package/src/caddy-redirects.test.ts +4 -1
- package/src/caddy-validate.test.ts +7 -1
- package/src/caddy.test.ts +39 -1
- package/src/caddy.ts +36 -4
- package/src/checkpoint-secrets.test.ts +14 -6
- package/src/cli-routing.test.ts +1 -1
- package/src/cli-routing.ts +1 -1
- package/src/cli.ts +71 -5
- package/src/cloudsql-tls.test.ts +255 -0
- package/src/cloudsql-tls.ts +359 -0
- package/src/compose.ts +193 -24
- package/src/converge-checkpoint.ts +8 -1
- package/src/converge-db-trust.test.ts +237 -0
- package/src/converge-db-trust.ts +212 -0
- package/src/converge-job-gcp.ts +11 -12
- package/src/converge-job-preflight.test.ts +5 -0
- package/src/converge-job-preflight.ts +8 -0
- package/src/converge-job.test.ts +70 -0
- package/src/converge-job.ts +32 -0
- package/src/database-tls-builders.test.ts +56 -0
- package/src/deployed-release.test.ts +107 -40
- package/src/deployed-release.ts +30 -9
- package/src/gateway-env-isolation.test.ts +50 -12
- package/src/gcp-names.ts +56 -0
- package/src/gcp-url-map.test.ts +6 -8
- package/src/gcp-url-map.ts +6 -2
- package/src/index.test.ts +10 -4
- package/src/index.ts +7 -0
- package/src/infra-contract.test.ts +7 -3
- package/src/infra-contract.ts +45 -1
- package/src/install-job.ts +4 -1
- package/src/install-state.ts +33 -4
- package/src/lifecycle.ts +23 -11
- package/src/mcp-auth-proxy-setup.test.ts +305 -0
- package/src/mcp-auth-proxy-setup.ts +374 -0
- package/src/mcp-iap-stacks.test.ts +1 -1
- package/src/media-volume.test.ts +1 -1
- package/src/migration-runner.ts +14 -9
- package/src/migration-targets.ts +12 -0
- package/src/operator-access-stacks.test.ts +1 -1
- package/src/operator-access.test.ts +20 -4
- package/src/operator-access.ts +14 -0
- package/src/pool-budget-stack.ts +15 -0
- package/src/pool-budget-stacks.test.ts +39 -14
- package/src/public-role-isolation.test.ts +18 -8
- package/src/public-role-isolation.ts +31 -17
- package/src/release-contract.ts +3 -0
- package/src/release-manifest.test.ts +25 -0
- package/src/release-manifest.ts +59 -4
- package/src/remote-mcp-stack-config.test.ts +19 -9
- package/src/remote-mcp.test.ts +252 -57
- package/src/remote-mcp.ts +336 -77
- package/src/runtime-secrets.test.ts +37 -14
- package/src/runtime-secrets.ts +87 -27
- package/src/scheduler-setup.test.ts +201 -0
- package/src/scheduler-setup.ts +375 -0
- package/src/seal-key-rotation.test.ts +2 -2
- package/src/seal-key-rotation.ts +25 -25
- package/src/self-hosted-backup.test.ts +1 -0
- package/src/self-hosted-compose.test.ts +180 -6
- package/src/self-hosted-db-tls.test.ts +111 -0
- package/src/self-hosted-db-tls.ts +287 -0
- package/src/self-hosted-release-resolve.test.ts +71 -0
- package/src/self-hosted-release.ts +41 -11
- package/src/self-hosted-roles.integration.test.ts +2 -0
- package/src/self-hosted-roles.test.ts +87 -3
- package/src/self-hosted-roles.ts +101 -3
- package/src/self-hosted-scheduler-interval.test.ts +66 -0
- package/src/self-hosted-scheduler-interval.ts +79 -0
- package/src/self-hosted-start.test.ts +35 -2
- package/src/self-hosted-start.ts +20 -2
- package/src/self-hosted-upgrade.test.ts +104 -14
- package/src/self-hosted-upgrade.ts +123 -31
- package/src/stack-contract-parity.test.ts +79 -12
- package/src/stack-contract.test.ts +199 -31
- package/src/stack-contract.ts +472 -45
- package/src/stack-converge-secrets.test.ts +28 -13
- package/src/stack-converge.test.ts +203 -28
- package/src/stack-converge.ts +92 -30
- package/src/staging-database.test.ts +114 -0
- package/src/staging-database.ts +148 -0
- package/src/state-store.test.ts +2 -1
- package/src/upgrade/app-roll.ts +97 -19
- package/src/upgrade/converge.ts +249 -19
- package/src/upgrade/health.test.ts +63 -25
- package/src/upgrade/health.ts +54 -22
- package/src/upgrade/image-forms.test.ts +34 -0
- package/src/upgrade/image-forms.ts +23 -8
- package/src/upgrade/release-images.test.ts +5 -1
- package/src/upgrade/release-images.ts +22 -6
- package/src/upgrade/upgrade.test.ts +438 -46
- package/src/upgrade/upgrade.ts +64 -6
- package/src/upgrader-gcp.ts +66 -3
- package/src/upgrader-iam-preflight.test.ts +19 -0
- package/src/upgrader-iam-preflight.ts +6 -0
- package/src/upgrader-job.test.ts +101 -27
- package/src/upgrader-job.ts +27 -14
- package/src/upgrader-records.test.ts +97 -12
- package/src/upgrader-records.ts +28 -11
- package/src/upgrader-setup.test.ts +2 -0
- package/src/upgrader-setup.ts +10 -4
- package/src/wizard.ts +19 -0
- package/src/wizards/gcp-cost.test.ts +103 -0
- package/src/wizards/gcp-cost.ts +125 -18
- package/src/wizards/gcp.ts +164 -22
- package/stacks/aws/Pulumi.yaml +1 -1
- package/stacks/aws/index.ts +72 -14
- package/stacks/azure/index.ts +108 -21
- package/stacks/gcp/Pulumi.yaml +1 -1
- package/stacks/gcp/README.md +1 -0
- package/stacks/gcp/index.ts +133 -24
- package/stacks/gcp-firebase/Pulumi.yaml +1 -1
- package/stacks/gcp-firebase/index.ts +212 -34
- package/stacks/self-hosted/Pulumi.yaml +3 -0
- package/stacks/self-hosted/README.md +6 -1
- package/stacks/self-hosted/index.ts +40 -5
package/src/upgrade/upgrade.ts
CHANGED
|
@@ -4,14 +4,17 @@
|
|
|
4
4
|
* `cms-provision upgrade` for one gcp / gcp-firebase install: converge, then
|
|
5
5
|
* app-roll (epic #696). The order, and why:
|
|
6
6
|
*
|
|
7
|
-
* 1. plan
|
|
7
|
+
* 1. plan: release digests, the services, the env contract — read-only
|
|
8
|
+
* but for the services an older install lacks (#1328, additive and
|
|
9
|
+
* serving nothing yet: mcp-auth-proxy-setup.ts)
|
|
8
10
|
* 2. verify the images' signatures
|
|
9
11
|
* 3. pre-upgrade Cloud SQL backup — the first change; if it fails, the
|
|
10
12
|
* install is exactly as it was
|
|
11
13
|
* 4. converge, the part before the roll (keyless provisioner, IAM, secrets,
|
|
12
14
|
* MCP access)
|
|
13
15
|
* 5. app-roll: health baseline, migrations, the converge steps that must
|
|
14
|
-
* follow migrations, admin
|
|
16
|
+
* follow migrations, admin, gateway and MCP-Auth-Proxy rolls, deep
|
|
17
|
+
* health check, rollback
|
|
15
18
|
* on failure, recorded digests
|
|
16
19
|
* 6. converge, the part after the roll; a complete converge records the
|
|
17
20
|
* install's infrastructure contract version
|
|
@@ -29,6 +32,7 @@
|
|
|
29
32
|
|
|
30
33
|
import type { GcloudRunner, Sleep } from "../gcloud-retry.js";
|
|
31
34
|
import type { ImageDigests } from "../install-state.js";
|
|
35
|
+
import { ensureReleaseServices } from "../mcp-auth-proxy-setup.js";
|
|
32
36
|
import type { HttpFetch } from "../runtime-secrets.js";
|
|
33
37
|
import {
|
|
34
38
|
type AppRollPlan,
|
|
@@ -53,6 +57,12 @@ export interface GcpUpgradeTarget {
|
|
|
53
57
|
readonly domain: string;
|
|
54
58
|
readonly stagingHost: string;
|
|
55
59
|
readonly ownerEmail: string;
|
|
60
|
+
/**
|
|
61
|
+
* #1140 — install.json's `schedulerIntervalMinutes` (absent: the default).
|
|
62
|
+
* Bounded to 1–15 by the converge, so a tampered record can change only
|
|
63
|
+
* how soon due work runs.
|
|
64
|
+
*/
|
|
65
|
+
readonly schedulerIntervalMinutes?: number;
|
|
56
66
|
/** The tag rolled to: a version (`0.11.0`) or a channel tag (`latest`, `rc`). */
|
|
57
67
|
readonly targetTag: string;
|
|
58
68
|
/** The infrastructure contract version this CLI converges to (infra-contract.ts). */
|
|
@@ -75,9 +85,17 @@ export interface GcpUpgradeTarget {
|
|
|
75
85
|
*/
|
|
76
86
|
export interface UpgradeCheckpoint {
|
|
77
87
|
readonly backup?: SqlBackup;
|
|
78
|
-
readonly priorRevisions?:
|
|
88
|
+
readonly priorRevisions?: PriorRevisions;
|
|
79
89
|
}
|
|
80
90
|
|
|
91
|
+
/**
|
|
92
|
+
* The rollback target per service. The MCP-Auth-Proxy's is absent in a
|
|
93
|
+
* checkpoint a release before #1328 wrote; its planned revision is kept then.
|
|
94
|
+
*/
|
|
95
|
+
export type PriorRevisions = Readonly<Record<"admin" | "gateway", string>> & {
|
|
96
|
+
readonly "mcp-auth-proxy"?: string;
|
|
97
|
+
};
|
|
98
|
+
|
|
81
99
|
export interface GcpUpgradeDeps {
|
|
82
100
|
readonly run: GcloudRunner;
|
|
83
101
|
readonly reporter: UpgradeReporter;
|
|
@@ -165,6 +183,41 @@ export async function upgradeGcpInstallOutcome(
|
|
|
165
183
|
r.error(images.error);
|
|
166
184
|
return failed;
|
|
167
185
|
}
|
|
186
|
+
// #1328 — the services the plan must find: the MCP-Auth-Proxy's account
|
|
187
|
+
// and, on an install whose stack predates it, the data gateway
|
|
188
|
+
// (gcp-firebase) or the proxy's own service (gcp). Additive, serving
|
|
189
|
+
// nothing yet, so the install keeps running as it is when this or
|
|
190
|
+
// anything after it fails. The gcp proxy runs the new release's image,
|
|
191
|
+
// so that image's signature is checked before it is deployed.
|
|
192
|
+
const proxyImage = images.images["mcp-auth-proxy"];
|
|
193
|
+
if (
|
|
194
|
+
target.provider === "gcp" &&
|
|
195
|
+
!target.skipVerify &&
|
|
196
|
+
!(await deps.verifyImages([{ slug: "mcp-auth-proxy", digest: proxyImage.digest }]))
|
|
197
|
+
) {
|
|
198
|
+
sPre.failed("The MCP-Auth-Proxy image's signature did not verify");
|
|
199
|
+
return failed;
|
|
200
|
+
}
|
|
201
|
+
const ensured = await ensureReleaseServices(
|
|
202
|
+
{
|
|
203
|
+
provider: target.provider,
|
|
204
|
+
projectId,
|
|
205
|
+
region: target.region,
|
|
206
|
+
env: target.env,
|
|
207
|
+
domain: target.domain,
|
|
208
|
+
mcpAuthProxyImageRef: proxyImage.imageRef,
|
|
209
|
+
},
|
|
210
|
+
{ run },
|
|
211
|
+
);
|
|
212
|
+
const ensureFailed = ensured.filter((o) => o.status === "failed");
|
|
213
|
+
if (ensureFailed.length > 0) {
|
|
214
|
+
sPre.failed("The services this release runs could not be set up. Nothing was changed.");
|
|
215
|
+
for (const o of ensureFailed) r.error(` FAILED: ${o.id}\n ${o.error ?? ""}`);
|
|
216
|
+
return failed;
|
|
217
|
+
}
|
|
218
|
+
for (const o of ensured.filter((o) => o.status === "applied")) {
|
|
219
|
+
r.info(` created: ${o.id}${o.detail ? ` (${o.detail})` : ""}`);
|
|
220
|
+
}
|
|
168
221
|
const planned = await planAppRoll(
|
|
169
222
|
{
|
|
170
223
|
provider: target.provider,
|
|
@@ -229,6 +282,9 @@ export async function upgradeGcpInstallOutcome(
|
|
|
229
282
|
const opsImageRef = await verifiedOpsImage(target, deps);
|
|
230
283
|
const converge = startConverge(plan, target.ownerEmail, {
|
|
231
284
|
opsImageRef,
|
|
285
|
+
...(target.schedulerIntervalMinutes !== undefined
|
|
286
|
+
? { schedulerIntervalMinutes: target.schedulerIntervalMinutes }
|
|
287
|
+
: {}),
|
|
232
288
|
run,
|
|
233
289
|
reporter: r,
|
|
234
290
|
...(deps.sleep ? { sleep: deps.sleep } : {}),
|
|
@@ -269,7 +325,9 @@ export async function upgradeGcpInstallOutcome(
|
|
|
269
325
|
},
|
|
270
326
|
);
|
|
271
327
|
if (!rolled.ok) return { ok: false, rolledBack: rolled.rolledBack };
|
|
272
|
-
r.success(
|
|
328
|
+
r.success(
|
|
329
|
+
`Upgrade to ${target.targetTag} complete (admin, gateway and MCP-Auth-Proxy revisions serving).`,
|
|
330
|
+
);
|
|
273
331
|
r.info(
|
|
274
332
|
`Pre-upgrade backup: ${backup.backup.backupId} (restore: ${restoreCommand(backup.backup)})`,
|
|
275
333
|
);
|
|
@@ -314,8 +372,8 @@ function withPriorRevisions(
|
|
|
314
372
|
return {
|
|
315
373
|
...plan,
|
|
316
374
|
services: plan.services.map((s) => {
|
|
317
|
-
const before = recorded[s.slug];
|
|
318
|
-
if (before === s.priorRevision) return s;
|
|
375
|
+
const before: string | undefined = recorded[s.slug];
|
|
376
|
+
if (before === undefined || before === s.priorRevision) return s;
|
|
319
377
|
r.info(
|
|
320
378
|
`Continuing an interrupted update: ${s.slug} goes back to ${before} (what served before it started) if the new release fails its checks.`,
|
|
321
379
|
);
|
package/src/upgrader-gcp.ts
CHANGED
|
@@ -16,6 +16,13 @@
|
|
|
16
16
|
import { mkdtempSync } from "node:fs";
|
|
17
17
|
import { tmpdir } from "node:os";
|
|
18
18
|
import { join } from "node:path";
|
|
19
|
+
import { isIpLiteral } from "@caelo-cms/shared";
|
|
20
|
+
import {
|
|
21
|
+
ensureCloudSqlVerifyFull,
|
|
22
|
+
liveDatabaseUrls,
|
|
23
|
+
moveServicesToDnsHost,
|
|
24
|
+
} from "./cloudsql-tls.js";
|
|
25
|
+
import { findSqlInstance } from "./gateway-credentials.js";
|
|
19
26
|
import { gcloud } from "./gcloud.js";
|
|
20
27
|
import { type GcloudRunner, realSleep } from "./gcloud-retry.js";
|
|
21
28
|
import { GCP_STACK_ENV } from "./gcp-names.js";
|
|
@@ -23,10 +30,16 @@ import type { InstallMetadata } from "./install-state.js";
|
|
|
23
30
|
import { readAdminConfig, runMigrationsViaCloudRunJob } from "./migration-runner.js";
|
|
24
31
|
import { fetchVerifiedReleaseManifest } from "./release-manifest.js";
|
|
25
32
|
import type { HttpFetch } from "./runtime-secrets.js";
|
|
33
|
+
import { DATABASE_URL_VARS } from "./stack-contract.js";
|
|
34
|
+
import { liveContainerEnv } from "./stack-converge.js";
|
|
26
35
|
import { opsBucketName } from "./state-backend.js";
|
|
27
36
|
import { acquireBucketStateLock, GcsStateStore, realSyncGcloud } from "./state-store.js";
|
|
28
|
-
import type { AppRollPlan } from "./upgrade/app-roll.js";
|
|
29
|
-
import {
|
|
37
|
+
import type { AppRollPlan, RollSlug } from "./upgrade/app-roll.js";
|
|
38
|
+
import {
|
|
39
|
+
describeServiceJson,
|
|
40
|
+
findServiceUrl,
|
|
41
|
+
resolveGcpResourceName,
|
|
42
|
+
} from "./upgrade/gcp-lookup.js";
|
|
30
43
|
import { createIapHealthProbe } from "./upgrade/health.js";
|
|
31
44
|
import { createReleaseImageForms } from "./upgrade/image-forms.js";
|
|
32
45
|
import { type IamTester, restIamTester } from "./upgrader-iam-preflight.js";
|
|
@@ -58,6 +71,55 @@ export async function upgraderDatabaseHost(
|
|
|
58
71
|
return cfg.databaseHost;
|
|
59
72
|
}
|
|
60
73
|
|
|
74
|
+
/**
|
|
75
|
+
* #1338 — the database host for the converge job (#819), after making sure
|
|
76
|
+
* a verify-full connection to it can work. On an install whose services
|
|
77
|
+
* still dial the instance's IP it runs the converge's database TLS step
|
|
78
|
+
* (cloudsql-tls.ts: shared CA, private DNS name, services moved onto it).
|
|
79
|
+
* Stage 2 of the job runs it as a timeline step, over a queue opened on
|
|
80
|
+
* the IP (converge-db-trust.ts, #1373), then reconnects on the host this
|
|
81
|
+
* returns.
|
|
82
|
+
* The job runs as the install's `caelo-provisioner`, which holds
|
|
83
|
+
* `roles/cloudsql.admin` and `roles/dns.admin` (gcloud.ts PROVISIONER_ROLES).
|
|
84
|
+
* Idempotent: the converge's own step finds everything in shape.
|
|
85
|
+
*/
|
|
86
|
+
export async function convergeDatabaseHost(
|
|
87
|
+
placement: Pick<UpgraderPlacement, "projectId" | "region">,
|
|
88
|
+
run: GcloudRunner = gcloud,
|
|
89
|
+
http?: HttpFetch,
|
|
90
|
+
): Promise<string> {
|
|
91
|
+
const host = await upgraderDatabaseHost(placement, run);
|
|
92
|
+
if (!isIpLiteral(host)) return host;
|
|
93
|
+
const install = { projectId: placement.projectId, env: GCP_STACK_ENV };
|
|
94
|
+
const lookup = await findSqlInstance(install, { run });
|
|
95
|
+
if (lookup.status !== "found") {
|
|
96
|
+
throw new Error(`could not find the install's Cloud SQL instance (${lookup.status})`);
|
|
97
|
+
}
|
|
98
|
+
const deps = { run, ...(http ? { http } : {}) };
|
|
99
|
+
const tls = await ensureCloudSqlVerifyFull({ ...install, sqlInstance: lookup.name }, deps);
|
|
100
|
+
if (!tls.ok) throw new Error(`database TLS could not be set up: ${tls.error}`);
|
|
101
|
+
const services: { serviceName: string; liveUrls: Record<string, string> }[] = [];
|
|
102
|
+
for (const slug of ["admin", "gateway"] as const) {
|
|
103
|
+
const found = await resolveGcpResourceName(
|
|
104
|
+
run,
|
|
105
|
+
"run-service",
|
|
106
|
+
`caelo-${GCP_STACK_ENV}-${slug}`,
|
|
107
|
+
placement.projectId,
|
|
108
|
+
placement.region,
|
|
109
|
+
);
|
|
110
|
+
if (found.status !== "found") throw new Error(`could not find the ${slug} service`);
|
|
111
|
+
const json = await describeServiceJson(run, placement.projectId, placement.region, found.name);
|
|
112
|
+
if (!json) throw new Error(`could not read the ${slug} service's configuration`);
|
|
113
|
+
services.push({
|
|
114
|
+
serviceName: found.name,
|
|
115
|
+
liveUrls: liveDatabaseUrls(liveContainerEnv(json), DATABASE_URL_VARS[slug]),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const moved = await moveServicesToDnsHost(placement, services, tls.host, deps);
|
|
119
|
+
if (!moved.ok) throw new Error(moved.error);
|
|
120
|
+
return tls.host;
|
|
121
|
+
}
|
|
122
|
+
|
|
61
123
|
/**
|
|
62
124
|
* The IAP-backed deep health probe for `plan`'s install (the upgrader job;
|
|
63
125
|
* the converge job, #819, too). `accessJustChanged`: a converge right
|
|
@@ -71,7 +133,7 @@ export async function healthProbeFor(
|
|
|
71
133
|
context: { readonly accessJustChanged: boolean },
|
|
72
134
|
) {
|
|
73
135
|
const { install } = plan;
|
|
74
|
-
const urls: Partial<Record<
|
|
136
|
+
const urls: Partial<Record<RollSlug, string>> = {};
|
|
75
137
|
for (const s of plan.services) {
|
|
76
138
|
const url = await findServiceUrl(run, install.projectId, install.region, s.serviceName);
|
|
77
139
|
if (!url) {
|
|
@@ -88,6 +150,7 @@ export async function healthProbeFor(
|
|
|
88
150
|
domain: install.domain,
|
|
89
151
|
adminServiceUrl: urls.admin as string,
|
|
90
152
|
gatewayServiceUrl: urls.gateway as string,
|
|
153
|
+
mcpAuthProxyServiceUrl: urls["mcp-auth-proxy"] as string,
|
|
91
154
|
},
|
|
92
155
|
{ run, http, sleep: realSleep, accessJustChanged: context.accessJustChanged },
|
|
93
156
|
),
|
|
@@ -73,6 +73,11 @@ function setupGrants(): Map<string, Set<string>> {
|
|
|
73
73
|
`projects/${p}/serviceAccounts/caelo-production-gateway-sa@${p}.iam.gserviceaccount.com`,
|
|
74
74
|
of("roles/iam.serviceAccountUser"),
|
|
75
75
|
],
|
|
76
|
+
// #1328 — the MCP-Auth-Proxy's revisions run as its own account.
|
|
77
|
+
[
|
|
78
|
+
`projects/${p}/serviceAccounts/caelo-production-mcp-proxy-sa@${p}.iam.gserviceaccount.com`,
|
|
79
|
+
of("roles/iam.serviceAccountUser"),
|
|
80
|
+
],
|
|
76
81
|
[
|
|
77
82
|
`projects/${p}/serviceAccounts/caelo-production-upgrader@${p}.iam.gserviceaccount.com`,
|
|
78
83
|
of("roles/iam.serviceAccountUser"),
|
|
@@ -154,6 +159,20 @@ describe("upgraderAccessChecks", () => {
|
|
|
154
159
|
expect(preflightMissingReason(r.missing, UPGRADER_PREFLIGHT_VOICE)).toContain("capture job");
|
|
155
160
|
});
|
|
156
161
|
|
|
162
|
+
it("#1328: the MCP-Auth-Proxy's account is checked, since every app-roll rolls the proxy", async () => {
|
|
163
|
+
const proxySa =
|
|
164
|
+
"projects/acme/serviceAccounts/caelo-production-mcp-proxy-sa@acme.iam.gserviceaccount.com";
|
|
165
|
+
const grants = setupGrants();
|
|
166
|
+
grants.delete(proxySa);
|
|
167
|
+
const r = await runIamPreflight(upgraderAccessChecks(TARGET), tester(grants));
|
|
168
|
+
expect(r.kind).toBe("missing");
|
|
169
|
+
if (r.kind !== "missing") return;
|
|
170
|
+
expect(r.missing.map((m) => [resourceName(m.check.resource), m.missing])).toEqual([
|
|
171
|
+
[proxySa, ["iam.serviceAccounts.actAs"]],
|
|
172
|
+
]);
|
|
173
|
+
expect(preflightMissingReason(r.missing, UPGRADER_PREFLIGHT_VOICE)).toContain("MCP-Auth-Proxy");
|
|
174
|
+
});
|
|
175
|
+
|
|
157
176
|
it("never needs a permission only a forbidden role would give (no compute, no IAM admin)", () => {
|
|
158
177
|
const all = upgraderAccessChecks(TARGET).flatMap((c) => c.permissions);
|
|
159
178
|
for (const p of all) {
|
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
auditServiceAccountEmail,
|
|
55
55
|
captureServiceAccountEmail,
|
|
56
56
|
gatewayServiceAccountEmail,
|
|
57
|
+
mcpAuthProxyServiceAccountEmail,
|
|
57
58
|
mcpIapServiceAccountEmail,
|
|
58
59
|
runServiceAccountEmail,
|
|
59
60
|
upgraderJobName,
|
|
@@ -186,6 +187,11 @@ export function upgraderAccessChecks(target: {
|
|
|
186
187
|
resource: sa(gatewayServiceAccountEmail(projectId, env)),
|
|
187
188
|
permissions: ["iam.serviceAccounts.actAs"],
|
|
188
189
|
},
|
|
190
|
+
{
|
|
191
|
+
need: "run the MCP-Auth-Proxy as its service account",
|
|
192
|
+
resource: sa(mcpAuthProxyServiceAccountEmail(projectId, env)),
|
|
193
|
+
permissions: ["iam.serviceAccounts.actAs"],
|
|
194
|
+
},
|
|
189
195
|
{
|
|
190
196
|
// #875 — every app-roll moves the audit job onto the new admin image
|
|
191
197
|
// (upgrade/admin-image-jobs.ts), and upgrader-setup.ts grants actAs on
|
package/src/upgrader-job.test.ts
CHANGED
|
@@ -57,14 +57,16 @@ const revisionJson = (deployed: string | null, resolved: string) =>
|
|
|
57
57
|
status: { imageDigest: `${MIRROR}@${resolved}` },
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
/** Serving revisions deployed as `admin` / `gateway` (index digests). */
|
|
60
|
+
/** Serving revisions deployed as `admin` / `gateway` (index digests); the proxy serves D("3"). */
|
|
61
61
|
const servingAs = (admin: string, gateway: string): [RegExp, Answer] => [
|
|
62
62
|
/^run revisions describe (\S+)/,
|
|
63
63
|
(l) =>
|
|
64
64
|
ok(
|
|
65
65
|
l.includes("-admin-")
|
|
66
66
|
? revisionJson(admin, `sha256:${"e".repeat(64)}`)
|
|
67
|
-
:
|
|
67
|
+
: l.includes(PROXY)
|
|
68
|
+
? revisionJson(D("3"), P("3"))
|
|
69
|
+
: revisionJson(gateway, `sha256:${"d".repeat(64)}`),
|
|
68
70
|
),
|
|
69
71
|
];
|
|
70
72
|
|
|
@@ -79,13 +81,16 @@ const releaseTags = (slug: "admin" | "gateway", tags: Record<string, string>): [
|
|
|
79
81
|
];
|
|
80
82
|
|
|
81
83
|
const ADMIN = "caelo-production-admin-aaa";
|
|
82
|
-
|
|
84
|
+
// #1328 — gcp-firebase: the visitor gateway is the fixed-name data gateway,
|
|
85
|
+
// the MCP-Auth-Proxy the service that was the gateway (E13 a).
|
|
86
|
+
const GATEWAY = "caelo-production-data-gateway";
|
|
87
|
+
const PROXY = "caelo-production-gateway-bbb";
|
|
83
88
|
|
|
84
89
|
const MANIFEST: ReleaseManifest = {
|
|
85
90
|
manifestVersion: 1,
|
|
86
91
|
version: "0.11.0",
|
|
87
92
|
channel: "stable",
|
|
88
|
-
digests: { admin: D("a"), gateway: D("b"), ops: D("c") },
|
|
93
|
+
digests: { admin: D("a"), gateway: D("b"), ops: D("c"), "mcp-auth-proxy": D("d") },
|
|
89
94
|
minFromVersion: "0.10.0",
|
|
90
95
|
infraContractVersion: 2,
|
|
91
96
|
migrations: { contract: false, lastContractVersion: null, head: "0267_platform_updates" },
|
|
@@ -121,14 +126,23 @@ const META: InstallMetadata = {
|
|
|
121
126
|
ownerEmail: "owner@acme.com",
|
|
122
127
|
region: "europe-west1",
|
|
123
128
|
createdAt: "2026-01-01T00:00:00Z",
|
|
124
|
-
imageDigests: { admin: D("1"), gateway: D("2") },
|
|
129
|
+
imageDigests: { admin: D("1"), gateway: D("2"), "mcp-auth-proxy": D("3") },
|
|
125
130
|
releaseVersion: "0.10.45",
|
|
126
131
|
infraContractVersion: 2,
|
|
127
132
|
};
|
|
128
133
|
|
|
129
|
-
const manifestOf = (
|
|
134
|
+
const manifestOf = (
|
|
135
|
+
version: string,
|
|
136
|
+
admin: string,
|
|
137
|
+
gateway: string,
|
|
138
|
+
proxy = D("3"),
|
|
139
|
+
): ReleaseManifestResult => ({
|
|
130
140
|
ok: true,
|
|
131
|
-
manifest: {
|
|
141
|
+
manifest: {
|
|
142
|
+
...MANIFEST,
|
|
143
|
+
version,
|
|
144
|
+
digests: { admin, gateway, ops: D("c"), "mcp-auth-proxy": proxy },
|
|
145
|
+
},
|
|
132
146
|
});
|
|
133
147
|
|
|
134
148
|
const serviceJson = (image: string, env: unknown[]) =>
|
|
@@ -141,11 +155,13 @@ const ADMIN_JSON = serviceJson("registry/admin@sha256:old", [
|
|
|
141
155
|
{ name: "CAELO_FIREBASE_SITE", value: "caelo-production-site-abc123" },
|
|
142
156
|
{
|
|
143
157
|
name: "ADMIN_DATABASE_URL",
|
|
144
|
-
value:
|
|
158
|
+
value:
|
|
159
|
+
"postgresql://admin_role@db1.p-label.europe-west1.sql-psa.goog:5432/cms_admin?sslmode=verify-full&sslrootcert=/etc/caelo/db-ca/cloudsql-global.pem",
|
|
145
160
|
},
|
|
146
161
|
secretRef("ADMIN_DATABASE_PASSWORD", "postgres-password"),
|
|
147
162
|
]);
|
|
148
163
|
const GATEWAY_JSON = serviceJson("registry/gateway@sha256:old", []);
|
|
164
|
+
const PROXY_JSON = serviceJson("registry/mcp-auth-proxy@sha256:old", []);
|
|
149
165
|
|
|
150
166
|
type Answer = GcloudResult | ((line: string) => GcloudResult);
|
|
151
167
|
|
|
@@ -153,15 +169,23 @@ function fakeGcloud(events: string[], overrides: [RegExp, Answer][] = []) {
|
|
|
153
169
|
const rules: [RegExp, Answer][] = [
|
|
154
170
|
...overrides,
|
|
155
171
|
[/^run services list .*caelo-production-admin/, ok(`${ADMIN}\n`)],
|
|
156
|
-
[/^run services list .*caelo-production-gateway/, ok(`${GATEWAY}\n`)],
|
|
172
|
+
[/^run services list .*caelo-production-data-gateway/, ok(`${GATEWAY}\n`)],
|
|
173
|
+
[/^run services list .*caelo-production-gateway/, ok(`${PROXY}\n`)],
|
|
157
174
|
[/describe (\S+) .*revisionName/, (l) => ok(`${l.split(" ")[3]}-00001-old\n`)],
|
|
158
175
|
[new RegExp(`describe ${ADMIN} .*--format=json`), ok(ADMIN_JSON)],
|
|
159
176
|
[new RegExp(`describe ${GATEWAY} .*--format=json`), ok(GATEWAY_JSON)],
|
|
177
|
+
[new RegExp(`describe ${PROXY} .*--format=json`), ok(PROXY_JSON)],
|
|
160
178
|
[/describe (\S+) .*status\.url/, (l) => ok(`https://${l.split(" ")[3]}-xyz.a.run.app\n`)],
|
|
161
179
|
[
|
|
162
180
|
/^run revisions describe (\S+)/,
|
|
163
181
|
(l) =>
|
|
164
|
-
ok(
|
|
182
|
+
ok(
|
|
183
|
+
l.includes("-admin-")
|
|
184
|
+
? revisionJson(D("1"), P("1"))
|
|
185
|
+
: l.includes(PROXY)
|
|
186
|
+
? revisionJson(D("3"), P("3"))
|
|
187
|
+
: revisionJson(D("2"), P("2")),
|
|
188
|
+
),
|
|
165
189
|
],
|
|
166
190
|
[/^sql instances list/, ok("caelo-production-pg-abc\n")],
|
|
167
191
|
[/^sql backups list/, ok("1760000000000\n")],
|
|
@@ -307,9 +331,11 @@ function fakeImageForms(order: string[], down: boolean): ReleaseImageForms {
|
|
|
307
331
|
return {
|
|
308
332
|
ofIndex,
|
|
309
333
|
of: async (m, slug) => {
|
|
334
|
+
const index = m.digests[slug];
|
|
335
|
+
if (!index) return { ok: false, transient: false, message: `no ${slug} image` };
|
|
310
336
|
const signed = m.platformDigests?.[slug];
|
|
311
|
-
if (signed) return { ok: true, digests: [
|
|
312
|
-
return ofIndex(slug,
|
|
337
|
+
if (signed) return { ok: true, digests: [index, signed], amd64: signed };
|
|
338
|
+
return ofIndex(slug, index);
|
|
313
339
|
},
|
|
314
340
|
};
|
|
315
341
|
}
|
|
@@ -393,6 +419,21 @@ describe("runUpgraderJob", () => {
|
|
|
393
419
|
]);
|
|
394
420
|
});
|
|
395
421
|
|
|
422
|
+
it("#1328: a target manifest without an MCP-Auth-Proxy image stops before anything changes", async () => {
|
|
423
|
+
const { "mcp-auth-proxy": _none, ...digests } = MANIFEST.digests;
|
|
424
|
+
const { deps, order, q, written } = setup({
|
|
425
|
+
manifest: { ok: true, manifest: { ...MANIFEST, digests } },
|
|
426
|
+
});
|
|
427
|
+
const r = await runUpgraderJob(deps);
|
|
428
|
+
expect(r.status).toBe("failed");
|
|
429
|
+
expect(q.end()?.codes).toEqual(["manifest"]);
|
|
430
|
+
expect(q.end()?.reason).toContain("names no mcp-auth-proxy image");
|
|
431
|
+
expect(order.some((l) => l.startsWith("gcloud sql backups create"))).toBe(false);
|
|
432
|
+
expect(order.some((l) => l.startsWith("migrate "))).toBe(false);
|
|
433
|
+
expect(order.some((l) => l.startsWith("gcloud run services update"))).toBe(false);
|
|
434
|
+
expect(written).toEqual([]);
|
|
435
|
+
});
|
|
436
|
+
|
|
396
437
|
it("#815: decides first, locks right before the backup, holds the lock to the end", async () => {
|
|
397
438
|
const ok = setup();
|
|
398
439
|
await runUpgraderJob(ok.deps);
|
|
@@ -429,6 +470,7 @@ describe("runUpgraderJob", () => {
|
|
|
429
470
|
"iam projects/acme/serviceAccounts/caelo-production-audit@acme.iam.gserviceaccount.com",
|
|
430
471
|
"iam projects/acme/serviceAccounts/caelo-production-capture@acme.iam.gserviceaccount.com",
|
|
431
472
|
"iam projects/acme/serviceAccounts/caelo-production-gateway-sa@acme.iam.gserviceaccount.com",
|
|
473
|
+
"iam projects/acme/serviceAccounts/caelo-production-mcp-proxy-sa@acme.iam.gserviceaccount.com",
|
|
432
474
|
"iam projects/acme/serviceAccounts/caelo-production-run-sa@acme.iam.gserviceaccount.com",
|
|
433
475
|
"iam projects/acme/serviceAccounts/caelo-production-upgrader@acme.iam.gserviceaccount.com",
|
|
434
476
|
"iam projects/caelo-website/locations/europe-west1/repositories/caelo-cms-images",
|
|
@@ -616,12 +658,21 @@ describe("runUpgraderJob", () => {
|
|
|
616
658
|
|
|
617
659
|
it("a revision deployed by tag matches by the digest Cloud Run resolved", async () => {
|
|
618
660
|
const { deps, written } = setup({
|
|
619
|
-
meta: {
|
|
661
|
+
meta: {
|
|
662
|
+
...META,
|
|
663
|
+
imageDigests: { admin: P("1"), gateway: D("2"), "mcp-auth-proxy": D("3") },
|
|
664
|
+
},
|
|
620
665
|
gcloud: [
|
|
621
666
|
[
|
|
622
667
|
/^run revisions describe (\S+)/,
|
|
623
668
|
(l) =>
|
|
624
|
-
ok(
|
|
669
|
+
ok(
|
|
670
|
+
l.includes("-admin-")
|
|
671
|
+
? revisionJson(null, P("1"))
|
|
672
|
+
: l.includes(PROXY)
|
|
673
|
+
? revisionJson(D("3"), P("3"))
|
|
674
|
+
: revisionJson(D("2"), P("2")),
|
|
675
|
+
),
|
|
625
676
|
],
|
|
626
677
|
],
|
|
627
678
|
});
|
|
@@ -664,14 +715,20 @@ describe("runUpgraderJob", () => {
|
|
|
664
715
|
|
|
665
716
|
it("#815: drift + a stop verdict: the records are corrected under the lock, the verdict stands", async () => {
|
|
666
717
|
const drift = {
|
|
667
|
-
meta: {
|
|
718
|
+
meta: {
|
|
719
|
+
...META,
|
|
720
|
+
imageDigests: { admin: D("9"), gateway: D("2"), "mcp-auth-proxy": D("3") },
|
|
721
|
+
infraContractVersion: 1,
|
|
722
|
+
},
|
|
668
723
|
manifests: { "0.10.45": manifestOf("0.10.45", D("1"), D("2")) },
|
|
669
724
|
};
|
|
670
725
|
const free = setup(drift);
|
|
671
726
|
expect((await runUpgraderJob(free.deps)).status).toBe("needs_cloud_shell");
|
|
672
727
|
expect(free.q.end()?.codes).toEqual(["infra-contract"]);
|
|
673
728
|
expect(free.written).toEqual([
|
|
674
|
-
expect.objectContaining({
|
|
729
|
+
expect.objectContaining({
|
|
730
|
+
imageDigests: { admin: D("1"), gateway: D("2"), "mcp-auth-proxy": D("3") },
|
|
731
|
+
}),
|
|
675
732
|
]);
|
|
676
733
|
expect(free.order.indexOf("lock")).toBeGreaterThan(step(free.order, /^manifest 0\.11\.0/));
|
|
677
734
|
expect(free.order).toContain("unlock");
|
|
@@ -814,7 +871,8 @@ describe("runUpgraderJob", () => {
|
|
|
814
871
|
const servingPlatform = (admin: string, gateway: string): [RegExp, Answer] => [
|
|
815
872
|
/^run revisions describe (\S+)/,
|
|
816
873
|
(l) => {
|
|
817
|
-
|
|
874
|
+
// The MCP-Auth-Proxy serves its recorded index D("3") throughout.
|
|
875
|
+
const d = l.includes("-admin-") ? admin : l.includes(PROXY) ? D("3") : gateway;
|
|
818
876
|
return ok(revisionJson(d, d));
|
|
819
877
|
},
|
|
820
878
|
];
|
|
@@ -824,12 +882,17 @@ describe("runUpgraderJob", () => {
|
|
|
824
882
|
const v064 = {
|
|
825
883
|
...MANIFEST,
|
|
826
884
|
version: "0.10.64",
|
|
827
|
-
digests: {
|
|
885
|
+
digests: {
|
|
886
|
+
admin: ADMIN_INDEX,
|
|
887
|
+
gateway: GATEWAY_INDEX,
|
|
888
|
+
ops: D("c"),
|
|
889
|
+
"mcp-auth-proxy": D("3"),
|
|
890
|
+
},
|
|
828
891
|
};
|
|
829
892
|
const { deps, q, written } = setup({
|
|
830
893
|
meta: {
|
|
831
894
|
...META,
|
|
832
|
-
imageDigests: { admin: ADMIN_INDEX, gateway: GATEWAY_INDEX },
|
|
895
|
+
imageDigests: { admin: ADMIN_INDEX, gateway: GATEWAY_INDEX, "mcp-auth-proxy": D("3") },
|
|
833
896
|
releaseVersion: "0.10.64",
|
|
834
897
|
},
|
|
835
898
|
manifests: { "0.10.64": { ok: true, manifest: v064 } },
|
|
@@ -839,7 +902,7 @@ describe("runUpgraderJob", () => {
|
|
|
839
902
|
expect((await runUpgraderJob(deps)).status).toBe("succeeded");
|
|
840
903
|
expect(written[0]).toMatchObject({
|
|
841
904
|
releaseVersion: "0.10.64",
|
|
842
|
-
imageDigests: { admin: ADMIN_INDEX, gateway: GATEWAY_INDEX },
|
|
905
|
+
imageDigests: { admin: ADMIN_INDEX, gateway: GATEWAY_INDEX, "mcp-auth-proxy": D("3") },
|
|
843
906
|
platformDigests: { admin: ADMIN_AMD64, gateway: GATEWAY_AMD64 },
|
|
844
907
|
});
|
|
845
908
|
expect(q.events.some((e) => e.message.startsWith("0.10.64 → 0.11.0"))).toBe(true);
|
|
@@ -861,6 +924,7 @@ describe("runUpgraderJob", () => {
|
|
|
861
924
|
expect(order.filter((l) => l.startsWith("index "))).toEqual([
|
|
862
925
|
`index admin@${D("a")}`,
|
|
863
926
|
`index gateway@${D("b")}`,
|
|
927
|
+
`index mcp-auth-proxy@${D("d")}`,
|
|
864
928
|
]);
|
|
865
929
|
expect(q.events.some((e) => e.message.includes("refreshed its install records"))).toBe(false);
|
|
866
930
|
});
|
|
@@ -868,14 +932,14 @@ describe("runUpgraderJob", () => {
|
|
|
868
932
|
it("the roll records the new release's platform forms, signed ones first", async () => {
|
|
869
933
|
const signed = {
|
|
870
934
|
...MANIFEST,
|
|
871
|
-
platformDigests: { admin: P("x"), gateway: P("y"), ops: P("z") },
|
|
935
|
+
platformDigests: { admin: P("x"), gateway: P("y"), ops: P("z"), "mcp-auth-proxy": P("w") },
|
|
872
936
|
};
|
|
873
937
|
const { deps, order, written } = setup({ manifest: { ok: true, manifest: signed } });
|
|
874
938
|
expect((await runUpgraderJob(deps)).status).toBe("succeeded");
|
|
875
939
|
expect(written.at(-1)).toMatchObject({
|
|
876
940
|
releaseVersion: "0.11.0",
|
|
877
|
-
imageDigests: { admin: D("a"), gateway: D("b") },
|
|
878
|
-
platformDigests: { admin: P("x"), gateway: P("y") },
|
|
941
|
+
imageDigests: { admin: D("a"), gateway: D("b"), "mcp-auth-proxy": D("d") },
|
|
942
|
+
platformDigests: { admin: P("x"), gateway: P("y"), "mcp-auth-proxy": P("w") },
|
|
879
943
|
});
|
|
880
944
|
expect(order.some((l) => l.startsWith("index "))).toBe(false);
|
|
881
945
|
});
|
|
@@ -886,7 +950,11 @@ describe("runUpgraderJob", () => {
|
|
|
886
950
|
imageForms: "down",
|
|
887
951
|
});
|
|
888
952
|
expect((await runUpgraderJob(deps)).status).toBe("succeeded");
|
|
889
|
-
expect(written.at(-1)?.imageDigests).toEqual({
|
|
953
|
+
expect(written.at(-1)?.imageDigests).toEqual({
|
|
954
|
+
admin: D("a"),
|
|
955
|
+
gateway: D("b"),
|
|
956
|
+
"mcp-auth-proxy": D("d"),
|
|
957
|
+
});
|
|
890
958
|
expect(written.at(-1)?.platformDigests).toBeUndefined();
|
|
891
959
|
expect(q.events.some((e) => e.level === "warn" && e.message.includes("linux/amd64"))).toBe(
|
|
892
960
|
true,
|
|
@@ -995,7 +1063,9 @@ describe("runUpgraderJob", () => {
|
|
|
995
1063
|
manifests: { "0.10.45": manifestOf("0.10.45", D("1"), D("2")) },
|
|
996
1064
|
});
|
|
997
1065
|
expect((await runUpgraderJob(deps)).status).toBe("needs_cloud_shell");
|
|
998
|
-
expect(q.end()?.reason).toContain(
|
|
1066
|
+
expect(q.end()?.reason).toContain(
|
|
1067
|
+
"The admin and the MCP-Auth-Proxy serve v0.10.45's image, the gateway does not",
|
|
1068
|
+
);
|
|
999
1069
|
expect(written).toEqual([]);
|
|
1000
1070
|
});
|
|
1001
1071
|
|
|
@@ -1021,8 +1091,12 @@ describe("runUpgraderJob", () => {
|
|
|
1021
1091
|
expect((await runUpgraderJob(deps)).status).toBe("needs_cloud_shell");
|
|
1022
1092
|
const end = q.end();
|
|
1023
1093
|
expect(end?.codes).toEqual(["install-state-drift"]);
|
|
1024
|
-
expect(end?.reason).toContain(
|
|
1025
|
-
|
|
1094
|
+
expect(end?.reason).toContain(
|
|
1095
|
+
"The gateway and the MCP-Auth-Proxy serve v0.10.45's image, the admin does not",
|
|
1096
|
+
);
|
|
1097
|
+
expect(end?.reason).toContain(
|
|
1098
|
+
"the admin and the MCP-Auth-Proxy serve v0.10.44's image, the gateway does not",
|
|
1099
|
+
);
|
|
1026
1100
|
expect(written).toEqual([]);
|
|
1027
1101
|
});
|
|
1028
1102
|
|