@caelo-cms/provisioning 0.10.29 → 0.10.31
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/admin-domain.d.ts +54 -0
- package/dist/admin-domain.d.ts.map +1 -0
- package/dist/admin-domain.js +122 -0
- package/dist/admin-domain.js.map +1 -0
- package/dist/cli.js +71 -7
- package/dist/cli.js.map +1 -1
- package/dist/compose.d.ts +7 -9
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +17 -1
- package/dist/compose.js.map +1 -1
- package/dist/deployed-release.d.ts +84 -0
- package/dist/deployed-release.d.ts.map +1 -0
- package/dist/deployed-release.js +182 -0
- package/dist/deployed-release.js.map +1 -0
- package/dist/gcloud-retry.d.ts +16 -0
- package/dist/gcloud-retry.d.ts.map +1 -0
- package/dist/gcloud-retry.js +17 -0
- package/dist/gcloud-retry.js.map +1 -0
- package/dist/gcloud.d.ts.map +1 -1
- package/dist/gcloud.js +2 -0
- package/dist/gcloud.js.map +1 -1
- package/dist/gcp-names.d.ts +58 -0
- package/dist/gcp-names.d.ts.map +1 -0
- package/dist/gcp-names.js +88 -0
- package/dist/gcp-names.js.map +1 -0
- package/dist/install-state.d.ts +31 -0
- package/dist/install-state.d.ts.map +1 -1
- package/dist/install-state.js +38 -0
- package/dist/install-state.js.map +1 -1
- package/dist/lifecycle.d.ts +5 -0
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +340 -50
- package/dist/lifecycle.js.map +1 -1
- package/dist/mcp-iap.d.ts +5 -26
- package/dist/mcp-iap.d.ts.map +1 -1
- package/dist/mcp-iap.js +6 -18
- package/dist/mcp-iap.js.map +1 -1
- package/dist/migration-runner.d.ts +25 -0
- package/dist/migration-runner.d.ts.map +1 -1
- package/dist/migration-runner.js +60 -19
- package/dist/migration-runner.js.map +1 -1
- package/dist/operator-access.d.ts +83 -0
- package/dist/operator-access.d.ts.map +1 -0
- package/dist/operator-access.js +562 -0
- package/dist/operator-access.js.map +1 -0
- package/dist/region-data.d.ts +30 -0
- package/dist/region-data.d.ts.map +1 -0
- package/dist/region-data.js +406 -0
- package/dist/region-data.js.map +1 -0
- package/dist/regions.d.ts +130 -0
- package/dist/regions.d.ts.map +1 -0
- package/dist/regions.js +247 -0
- package/dist/regions.js.map +1 -0
- package/dist/runtime-secrets.d.ts +128 -0
- package/dist/runtime-secrets.d.ts.map +1 -0
- package/dist/runtime-secrets.js +444 -0
- package/dist/runtime-secrets.js.map +1 -0
- package/dist/stack-contract.d.ts +231 -0
- package/dist/stack-contract.d.ts.map +1 -0
- package/dist/stack-contract.js +455 -0
- package/dist/stack-contract.js.map +1 -0
- package/dist/stack-converge.d.ts +183 -0
- package/dist/stack-converge.d.ts.map +1 -0
- package/dist/stack-converge.js +513 -0
- package/dist/stack-converge.js.map +1 -0
- package/dist/wizard.d.ts +7 -0
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +11 -1
- package/dist/wizard.js.map +1 -1
- package/dist/wizards/gcp-cost.d.ts +10 -10
- package/dist/wizards/gcp-cost.d.ts.map +1 -1
- package/dist/wizards/gcp-cost.js +40 -3
- package/dist/wizards/gcp-cost.js.map +1 -1
- package/dist/wizards/gcp-firebase.d.ts +2 -0
- package/dist/wizards/gcp-firebase.d.ts.map +1 -1
- package/dist/wizards/gcp-firebase.js +1 -0
- package/dist/wizards/gcp-firebase.js.map +1 -1
- package/dist/wizards/gcp-pulumi.d.ts +51 -1
- package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
- package/dist/wizards/gcp-pulumi.js +89 -6
- package/dist/wizards/gcp-pulumi.js.map +1 -1
- package/dist/wizards/gcp.d.ts +2 -0
- package/dist/wizards/gcp.d.ts.map +1 -1
- package/dist/wizards/gcp.js +171 -23
- package/dist/wizards/gcp.js.map +1 -1
- package/package.json +3 -3
- package/stacks/aws/Pulumi.yaml +5 -2
- package/stacks/aws/README.md +3 -1
- package/stacks/aws/index.ts +21 -1
- package/stacks/azure/Pulumi.yaml +6 -3
- package/stacks/azure/README.md +1 -1
- package/stacks/azure/index.ts +22 -3
- package/stacks/gcp/Pulumi.yaml +5 -5
- package/stacks/gcp/README.md +15 -3
- package/stacks/gcp/index.ts +204 -141
- package/stacks/gcp-firebase/Pulumi.yaml +5 -2
- package/stacks/gcp-firebase/index.ts +169 -76
- package/stacks/self-hosted/Pulumi.yaml +4 -0
- package/stacks/self-hosted/index.ts +3 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
/**
|
|
3
|
+
* What a GCP install looks like, declared once for both ways of producing
|
|
4
|
+
* one: the Pulumi stacks (`stacks/gcp`, `stacks/gcp-firebase`) for new
|
|
5
|
+
* installs and `pulumi up`, and `cms-provision upgrade` (stack-converge.ts)
|
|
6
|
+
* for existing installs, which rolls images through gcloud without running
|
|
7
|
+
* Pulumi.
|
|
8
|
+
*
|
|
9
|
+
* Before this contract, everything added to the stacks after an install was
|
|
10
|
+
* created never reached that install: `CAELO_SITE_URL` (so `site_base_url`
|
|
11
|
+
* stayed unset and Stage failed), the run SA's `run.viewer` on the gateway
|
|
12
|
+
* (Firebase Hosting deploys 403'd), the telemetry roles, the one-year CDN
|
|
13
|
+
* TTLs. Three parts close that gap:
|
|
14
|
+
*
|
|
15
|
+
* - {@link adminEnvContract} / {@link gatewayEnvContract}: the complete
|
|
16
|
+
* env of the two Cloud Run services — plain values and Secret Manager
|
|
17
|
+
* references. The stacks deploy them as the Cloud Run `envs`; upgrade
|
|
18
|
+
* applies them (`--update-env-vars` / `--update-secrets`) in the same
|
|
19
|
+
* `services update` that rolls the image.
|
|
20
|
+
* - {@link stackIamInvariants} + {@link STATIC_CDN_POLICY}: the IAM
|
|
21
|
+
* bindings and CDN settings upgrade ensures with gcloud.
|
|
22
|
+
* - {@link STACK_IAM_NOT_ENSURED}: bindings upgrade deliberately leaves to
|
|
23
|
+
* Pulumi, each with the reason. The parity test
|
|
24
|
+
* (stack-contract-parity.test.ts) fails when a stack declares an IAM
|
|
25
|
+
* binding that is in neither list, so a new binding cannot ship without
|
|
26
|
+
* a decision about existing installs.
|
|
27
|
+
*
|
|
28
|
+
* No secret value is ever a plain env var: database URLs carry no password
|
|
29
|
+
* (the apps add it from the `<NAME>_PASSWORD` var, @caelo-cms/shared
|
|
30
|
+
* `databaseUrlFromEnv`), and every secret is a `secretKeyRef` to Secret
|
|
31
|
+
* Manager. Anyone with `run.services.get` sees secret names, never values.
|
|
32
|
+
*
|
|
33
|
+
* Pure module — the stacks import it from `dist/`.
|
|
34
|
+
*/
|
|
35
|
+
import { gatewayServiceAccountEmail, gcpBucketName, gcpNamePrefix, gcpSecretId, mcpIapServiceAccountEmail, operatorAccessJobResource, runServiceAccountEmail, staticPublisherServiceAccountId, } from "./gcp-names.js";
|
|
36
|
+
/** Port Cloud SQL Postgres listens on (private IP). */
|
|
37
|
+
const DATABASE_PORT = 5432;
|
|
38
|
+
/**
|
|
39
|
+
* Password-less connection URLs for a Cloud SQL instance at `host`. The
|
|
40
|
+
* password reaches the apps separately, as a Secret Manager reference
|
|
41
|
+
* ({@link SERVICE_SECRET_ENV}).
|
|
42
|
+
*/
|
|
43
|
+
export function databaseUrls(host) {
|
|
44
|
+
const url = (role, db) => `postgresql://${role}@${host}:${DATABASE_PORT}/${db}?sslmode=require`;
|
|
45
|
+
return {
|
|
46
|
+
admin: url("admin_role", "cms_admin"),
|
|
47
|
+
publicAdmin: url("admin_role", "cms_public"),
|
|
48
|
+
public: url("public_role", "cms_public"),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** The public site's base URL, as the admin seeds `site_base_url` from it (#551). */
|
|
52
|
+
export function publicSiteUrl(domain) {
|
|
53
|
+
return `https://${domain}`;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The admin env var naming the MCP service account (issue #37). `upgrade`
|
|
57
|
+
* leaves it untouched when it could not set that account up.
|
|
58
|
+
*/
|
|
59
|
+
export const MCP_ENV_VAR = "CAELO_MCP_IAP_SERVICE_ACCOUNT";
|
|
60
|
+
/**
|
|
61
|
+
* The admin env var naming the operator-access sync job. `upgrade` leaves it
|
|
62
|
+
* untouched when it could not set the job up.
|
|
63
|
+
*/
|
|
64
|
+
export const OPERATOR_ACCESS_JOB_ENV_VAR = "CAELO_OPERATOR_ACCESS_JOB";
|
|
65
|
+
/** Where the admin image ships the static-generator CLI. */
|
|
66
|
+
const GENERATOR_CLI = "/app/apps/static-generator/src/cli.ts";
|
|
67
|
+
/**
|
|
68
|
+
* Secrets the CLI generates and stores (runtime-secrets.ts
|
|
69
|
+
* `ensureGeneratedSecrets`) rather than Pulumi: the wizard creates them
|
|
70
|
+
* before `pulumi up`, upgrade creates them on installs that predate them.
|
|
71
|
+
* Pulumi only references them, so a `pulumi up` after an upgrade never
|
|
72
|
+
* collides with a secret upgrade created.
|
|
73
|
+
*/
|
|
74
|
+
export const CLI_GENERATED_SECRETS = [
|
|
75
|
+
"internal-secret",
|
|
76
|
+
"tool-approval-secret",
|
|
77
|
+
];
|
|
78
|
+
/**
|
|
79
|
+
* Each service's Secret Manager-backed env vars and the secret each reads.
|
|
80
|
+
* The services' run SAs get `secretAccessor` on exactly these secrets
|
|
81
|
+
* ({@link runtimeSecretBindings}), so this table is also the least-privilege
|
|
82
|
+
* boundary between the two services.
|
|
83
|
+
*/
|
|
84
|
+
export const SERVICE_SECRET_ENV = {
|
|
85
|
+
admin: {
|
|
86
|
+
ADMIN_DATABASE_PASSWORD: "postgres-password",
|
|
87
|
+
PUBLIC_ADMIN_DATABASE_PASSWORD: "postgres-password",
|
|
88
|
+
// P18 — encrypts AI provider keys and other at-rest secrets in cms_admin.
|
|
89
|
+
CAELO_SECRET_KEK: "secret-kek",
|
|
90
|
+
// P15.1 — HMAC key of the /api/internal/* bearer tokens (internal-jwt.ts).
|
|
91
|
+
CAELO_INTERNAL_SECRET: "internal-secret",
|
|
92
|
+
// Binds an AI tool-approval to the exact call the operator approved
|
|
93
|
+
// (admin-core ai/providers/_sdk-shared.ts).
|
|
94
|
+
CAELO_TOOL_APPROVAL_SECRET: "tool-approval-secret",
|
|
95
|
+
},
|
|
96
|
+
gateway: {
|
|
97
|
+
// Known gap against CLAUDE.md §2 ("never let the API Gateway hold
|
|
98
|
+
// admin_role credentials"): the gateway reads site_settings, rate
|
|
99
|
+
// limits, captcha challenges, the request log and the plugin registry
|
|
100
|
+
// through an admin_role pool (apps/api-gateway/src/server.ts), and fails
|
|
101
|
+
// to boot without it. Until that moves behind a narrower role the
|
|
102
|
+
// gateway needs this credential; it is no longer a plain env var.
|
|
103
|
+
ADMIN_DATABASE_PASSWORD: "postgres-password",
|
|
104
|
+
PUBLIC_DATABASE_PASSWORD: "postgres-password",
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
/** The secrets `service` reads (deduplicated, table order). */
|
|
108
|
+
export function serviceSecrets(service) {
|
|
109
|
+
return [...new Set(Object.values(SERVICE_SECRET_ENV[service]))];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Env vars an older stack set that the contract no longer gives a service;
|
|
113
|
+
* upgrade removes them. The gateway never used the KEK, and its run SA
|
|
114
|
+
* cannot read it.
|
|
115
|
+
*/
|
|
116
|
+
export const RETIRED_SERVICE_ENV = {
|
|
117
|
+
admin: [],
|
|
118
|
+
gateway: ["CAELO_SECRET_KEK"],
|
|
119
|
+
};
|
|
120
|
+
function secretEnv(service, env) {
|
|
121
|
+
return Object.entries(SERVICE_SECRET_ENV[service]).map(([name, secret]) => ({
|
|
122
|
+
name,
|
|
123
|
+
valueSource: { secretKeyRef: { secret: gcpSecretId(env, secret), version: "latest" } },
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
// ===========================================================================
|
|
127
|
+
// Env contracts
|
|
128
|
+
// ===========================================================================
|
|
129
|
+
/** Env vars both services carry. */
|
|
130
|
+
function commonEnv(inputs) {
|
|
131
|
+
return [
|
|
132
|
+
{ name: "CAELO_PROVIDER", value: inputs.provider },
|
|
133
|
+
{ name: "CAELO_ENV", value: inputs.env },
|
|
134
|
+
{
|
|
135
|
+
name: "MEDIA_STORAGE_URL",
|
|
136
|
+
value: `gs://${gcpBucketName(inputs.projectId, inputs.env, "media")}`,
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The env of the gateway service. Order is stable so the stacks see no
|
|
142
|
+
* spurious diffs.
|
|
143
|
+
*/
|
|
144
|
+
export function gatewayEnvContract(inputs) {
|
|
145
|
+
return [
|
|
146
|
+
...commonEnv(inputs),
|
|
147
|
+
{ name: "PUBLIC_DATABASE_URL", value: inputs.databaseUrls.public },
|
|
148
|
+
// See the known gap noted on SERVICE_SECRET_ENV.gateway.
|
|
149
|
+
{ name: "ADMIN_DATABASE_URL", value: inputs.databaseUrls.admin },
|
|
150
|
+
...secretEnv("gateway", inputs.env),
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The env of the admin service.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* adminEnvContract({ provider: "gcp", projectId: "p", env: "production",
|
|
158
|
+
* domain: "example.com", region: "europe-west1",
|
|
159
|
+
* databaseUrls: databaseUrls("10.20.0.3") })
|
|
160
|
+
* // → [{ name: "CAELO_PROVIDER", value: "gcp" }, …,
|
|
161
|
+
* // { name: "CAELO_SITE_URL", value: "https://example.com" }, …,
|
|
162
|
+
* // { name: "CAELO_SECRET_KEK", valueSource: { secretKeyRef: … } }, …]
|
|
163
|
+
*/
|
|
164
|
+
export function adminEnvContract(inputs) {
|
|
165
|
+
const { projectId, env, domain } = inputs;
|
|
166
|
+
const admin = [
|
|
167
|
+
...commonEnv(inputs),
|
|
168
|
+
{ name: "ADMIN_DATABASE_URL", value: inputs.databaseUrls.admin },
|
|
169
|
+
// admin_role on cms_public: cross-DB reads, plugin DDL, migrations.
|
|
170
|
+
// Not public_role (write-limited).
|
|
171
|
+
{ name: "PUBLIC_ADMIN_DATABASE_URL", value: inputs.databaseUrls.publicAdmin },
|
|
172
|
+
// #551 — the admin seeds site_defaults.site_base_url from it (canonical,
|
|
173
|
+
// og:url, sitemap) when it is not configured yet.
|
|
174
|
+
{ name: "CAELO_SITE_URL", value: publicSiteUrl(domain) },
|
|
175
|
+
{ name: "CAELO_GENERATOR_CLI", value: GENERATOR_CLI },
|
|
176
|
+
// Issue #37 — shown in the /security/mcp `claude mcp add` command.
|
|
177
|
+
{ name: MCP_ENV_VAR, value: mcpIapServiceAccountEmail(projectId) },
|
|
178
|
+
// The operator-access sync job the admin starts after user/role changes
|
|
179
|
+
// (admin-core security/operator-access/gcp-job-trigger.ts).
|
|
180
|
+
{
|
|
181
|
+
name: OPERATOR_ACCESS_JOB_ENV_VAR,
|
|
182
|
+
value: operatorAccessJobResource(projectId, inputs.region, env),
|
|
183
|
+
},
|
|
184
|
+
];
|
|
185
|
+
if (inputs.provider === "gcp") {
|
|
186
|
+
// v0.2.78 — the GCS StaticPublisher: Stage uploads to staging,
|
|
187
|
+
// Confirm-publish copies to static.
|
|
188
|
+
admin.push({ name: "CAELO_STATIC_BUCKET", value: gcpBucketName(projectId, env, "static") }, { name: "CAELO_STAGING_BUCKET", value: gcpBucketName(projectId, env, "staging") });
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// v0.3.1 — the Firebase publisher deploys to this site and declares
|
|
192
|
+
// the /api/** rewrite to the gateway service.
|
|
193
|
+
admin.push({ name: "CAELO_FIREBASE_SITE", value: inputs.firebaseSiteId }, { name: "CAELO_GATEWAY_SERVICE", value: inputs.gatewayService }, { name: "CAELO_GATEWAY_REGION", value: inputs.region });
|
|
194
|
+
}
|
|
195
|
+
admin.push(...secretEnv("admin", env));
|
|
196
|
+
return admin;
|
|
197
|
+
}
|
|
198
|
+
// ===========================================================================
|
|
199
|
+
// IAM + CDN invariants
|
|
200
|
+
// ===========================================================================
|
|
201
|
+
/**
|
|
202
|
+
* Cloud CDN policy of the `gcp` stack's static backend bucket. The GCS
|
|
203
|
+
* publisher sets an explicit Cache-Control on every object: content-hashed
|
|
204
|
+
* assets `max-age=31536000, immutable`, pages `max-age=60` + SWR. In
|
|
205
|
+
* CACHE_ALL_STATIC mode `clientTtl` clamps the max-age browsers see and
|
|
206
|
+
* `maxTtl` caps the edge TTL, so both must allow a year or the immutable
|
|
207
|
+
* policy is cut back to 1h (#555). `defaultTtl` applies only to responses
|
|
208
|
+
* without a max-age.
|
|
209
|
+
*/
|
|
210
|
+
export const STATIC_CDN_POLICY = {
|
|
211
|
+
cacheMode: "CACHE_ALL_STATIC",
|
|
212
|
+
defaultTtl: 3600,
|
|
213
|
+
maxTtl: 31536000,
|
|
214
|
+
clientTtl: 31536000,
|
|
215
|
+
};
|
|
216
|
+
const SECRET_ACCESSOR = "roles/secretmanager.secretAccessor";
|
|
217
|
+
/**
|
|
218
|
+
* The `secretAccessor` bindings both stacks declare: each service's run SA
|
|
219
|
+
* on exactly the secrets its env reads ({@link SERVICE_SECRET_ENV}).
|
|
220
|
+
*/
|
|
221
|
+
export function runtimeSecretBindings() {
|
|
222
|
+
return ["admin", "gateway"].flatMap((service) => serviceSecrets(service).map((secret) => ({
|
|
223
|
+
service,
|
|
224
|
+
secret,
|
|
225
|
+
// The admin's keep the names they had when one SA served both.
|
|
226
|
+
stackResource: service === "admin" ? `${secret}-binding` : `gateway-${secret}-binding`,
|
|
227
|
+
})));
|
|
228
|
+
}
|
|
229
|
+
function secretAccessors() {
|
|
230
|
+
return runtimeSecretBindings().map((b) => ({
|
|
231
|
+
stackResource: b.stackResource,
|
|
232
|
+
role: SECRET_ACCESSOR,
|
|
233
|
+
member: b.service === "admin" ? "run-sa" : "gateway-sa",
|
|
234
|
+
target: { kind: "secret", name: b.secret },
|
|
235
|
+
onFailure: "abort",
|
|
236
|
+
why: `the ${b.service} reads ${b.secret} from Secret Manager at boot`,
|
|
237
|
+
}));
|
|
238
|
+
}
|
|
239
|
+
/** v0.6.6 — a custom runtime SA gets no telemetry roles implicitly. */
|
|
240
|
+
const TELEMETRY = [
|
|
241
|
+
{
|
|
242
|
+
stackResource: "run-log-writer",
|
|
243
|
+
role: "roles/logging.logWriter",
|
|
244
|
+
member: "run-sa",
|
|
245
|
+
target: { kind: "project" },
|
|
246
|
+
onFailure: "warn",
|
|
247
|
+
why: "container logs reach Cloud Logging",
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
stackResource: "run-metric-writer",
|
|
251
|
+
role: "roles/monitoring.metricWriter",
|
|
252
|
+
member: "run-sa",
|
|
253
|
+
target: { kind: "project" },
|
|
254
|
+
onFailure: "warn",
|
|
255
|
+
why: "CPU/memory metrics reach Cloud Monitoring (autoscaling signals)",
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
stackResource: "gateway-log-writer",
|
|
259
|
+
role: "roles/logging.logWriter",
|
|
260
|
+
member: "gateway-sa",
|
|
261
|
+
target: { kind: "project" },
|
|
262
|
+
onFailure: "warn",
|
|
263
|
+
why: "gateway container logs reach Cloud Logging",
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
stackResource: "gateway-metric-writer",
|
|
267
|
+
role: "roles/monitoring.metricWriter",
|
|
268
|
+
member: "gateway-sa",
|
|
269
|
+
target: { kind: "project" },
|
|
270
|
+
onFailure: "warn",
|
|
271
|
+
why: "gateway CPU/memory metrics reach Cloud Monitoring (autoscaling signals)",
|
|
272
|
+
},
|
|
273
|
+
];
|
|
274
|
+
const GCP_ONLY = [
|
|
275
|
+
{
|
|
276
|
+
stackResource: "static-public-read",
|
|
277
|
+
role: "roles/storage.objectViewer",
|
|
278
|
+
member: "allUsers",
|
|
279
|
+
target: { kind: "bucket", bucket: "static" },
|
|
280
|
+
onFailure: "abort",
|
|
281
|
+
why: "Cloud CDN fetches the public site from the static bucket",
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
stackResource: "media-rw",
|
|
285
|
+
role: "roles/storage.objectAdmin",
|
|
286
|
+
member: "run-sa",
|
|
287
|
+
target: { kind: "bucket", bucket: "media" },
|
|
288
|
+
onFailure: "abort",
|
|
289
|
+
why: "the admin stores and serves media",
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
stackResource: "admin-static-rw",
|
|
293
|
+
role: "roles/storage.objectAdmin",
|
|
294
|
+
member: "run-sa",
|
|
295
|
+
target: { kind: "bucket", bucket: "static" },
|
|
296
|
+
onFailure: "abort",
|
|
297
|
+
why: "Confirm-publish copies the staged build into the static bucket",
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
stackResource: "admin-staging-rw",
|
|
301
|
+
role: "roles/storage.objectAdmin",
|
|
302
|
+
member: "run-sa",
|
|
303
|
+
target: { kind: "bucket", bucket: "staging" },
|
|
304
|
+
onFailure: "abort",
|
|
305
|
+
why: "Stage uploads builds to the private staging bucket (v0.2.78)",
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
stackResource: "static-publisher-rw",
|
|
309
|
+
role: "roles/storage.objectAdmin",
|
|
310
|
+
member: "static-publisher-sa",
|
|
311
|
+
target: { kind: "bucket", bucket: "static" },
|
|
312
|
+
onFailure: "warn",
|
|
313
|
+
why: "manual `deploy` uploads as the static-publisher SA (the admin publishes without it)",
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
stackResource: "iap-invoke-admin",
|
|
317
|
+
role: "roles/run.invoker",
|
|
318
|
+
member: "iap-service-agent",
|
|
319
|
+
target: { kind: "run-service", service: "admin" },
|
|
320
|
+
onFailure: "abort",
|
|
321
|
+
why: "IAP forwards admin traffic to Cloud Run as its service agent",
|
|
322
|
+
},
|
|
323
|
+
];
|
|
324
|
+
const GCP_FIREBASE_ONLY = [
|
|
325
|
+
{
|
|
326
|
+
stackResource: "media-rw",
|
|
327
|
+
role: "roles/storage.objectAdmin",
|
|
328
|
+
member: "run-sa",
|
|
329
|
+
target: { kind: "bucket", bucket: "media" },
|
|
330
|
+
onFailure: "abort",
|
|
331
|
+
why: "the admin stores and serves media",
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
stackResource: "run-firebase-hosting",
|
|
335
|
+
role: "roles/firebasehosting.admin",
|
|
336
|
+
member: "run-sa",
|
|
337
|
+
target: { kind: "project" },
|
|
338
|
+
onFailure: "abort",
|
|
339
|
+
why: "the admin publishes the site to Firebase Hosting",
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
stackResource: "gateway-public-invoker",
|
|
343
|
+
role: "roles/run.invoker",
|
|
344
|
+
member: "allUsers",
|
|
345
|
+
target: { kind: "run-service", service: "gateway" },
|
|
346
|
+
onFailure: "abort",
|
|
347
|
+
why: "Firebase Hosting rewrites /api/** to the public gateway",
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
stackResource: "gateway-viewer-for-admin-sa",
|
|
351
|
+
role: "roles/run.viewer",
|
|
352
|
+
member: "run-sa",
|
|
353
|
+
target: { kind: "run-service", service: "gateway" },
|
|
354
|
+
onFailure: "abort",
|
|
355
|
+
why: "Firebase Hosting checks run.services.get on the rewrite target at deploy time (v0.6.5; without it every publish 403s)",
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
stackResource: "admin-iap-sa-invoker",
|
|
359
|
+
role: "roles/run.invoker",
|
|
360
|
+
member: "iap-service-agent",
|
|
361
|
+
target: { kind: "run-service", service: "admin" },
|
|
362
|
+
onFailure: "abort",
|
|
363
|
+
why: "IAP forwards admin traffic to Cloud Run as its service agent",
|
|
364
|
+
},
|
|
365
|
+
];
|
|
366
|
+
/** Every IAM binding upgrade ensures on an install of `provider`. */
|
|
367
|
+
export function stackIamInvariants(provider) {
|
|
368
|
+
return [
|
|
369
|
+
...secretAccessors(),
|
|
370
|
+
...(provider === "gcp" ? GCP_ONLY : GCP_FIREBASE_ONLY),
|
|
371
|
+
...TELEMETRY,
|
|
372
|
+
];
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* IAM bindings the stacks declare that upgrade deliberately does not ensure,
|
|
376
|
+
* keyed by provider and the stack resource's logical name as written in the
|
|
377
|
+
* stack source, with each `${…}` placeholder written as `*`.
|
|
378
|
+
*/
|
|
379
|
+
export const STACK_IAM_NOT_ENSURED = {
|
|
380
|
+
gcp: {
|
|
381
|
+
"admin-iap-allow-*": "The IAP allowlist is operator-managed membership from stack config; upgrade must not re-add principals an Owner removed.",
|
|
382
|
+
"mcp-token-creator-*": "Ensured by ensureMcpIapAccess (mcp-iap.ts).",
|
|
383
|
+
"admin-iap-mcp": "Ensured by ensureMcpIapAccess (mcp-iap.ts).",
|
|
384
|
+
"edge-log-sink-bq-perms": "The member is the sink's generated writer identity, which only Pulumi knows; analytics only.",
|
|
385
|
+
},
|
|
386
|
+
"gcp-firebase": {
|
|
387
|
+
"admin-iap-*": "The IAP allowlist is operator-managed membership from stack config; upgrade must not re-add principals an Owner removed.",
|
|
388
|
+
"admin-invoker-*": "Per-principal invoker for the IAP allowlist (operator-managed, see admin-iap-*).",
|
|
389
|
+
"mcp-token-creator-*": "Ensured by ensureMcpIapAccess (mcp-iap.ts).",
|
|
390
|
+
"admin-iap-mcp": "Ensured by ensureMcpIapAccess (mcp-iap.ts).",
|
|
391
|
+
},
|
|
392
|
+
};
|
|
393
|
+
/** The IAM member string for a principal on a concrete install. */
|
|
394
|
+
export function iamMember(principal, install) {
|
|
395
|
+
switch (principal) {
|
|
396
|
+
case "run-sa":
|
|
397
|
+
return `serviceAccount:${runServiceAccountEmail(install.projectId, install.env)}`;
|
|
398
|
+
case "gateway-sa":
|
|
399
|
+
return `serviceAccount:${gatewayServiceAccountEmail(install.projectId, install.env)}`;
|
|
400
|
+
case "static-publisher-sa":
|
|
401
|
+
return `serviceAccount:${staticPublisherServiceAccountId(install.env)}@${install.projectId}.iam.gserviceaccount.com`;
|
|
402
|
+
case "iap-service-agent":
|
|
403
|
+
return `serviceAccount:service-${install.projectNumber}@gcp-sa-iap.iam.gserviceaccount.com`;
|
|
404
|
+
case "allUsers":
|
|
405
|
+
return "allUsers";
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/** Name prefix of the `gcp` stack's static backend bucket (Pulumi auto-suffixed). */
|
|
409
|
+
export function staticBackendBucketPrefix(env) {
|
|
410
|
+
return `${gcpNamePrefix(env)}-static-backend`;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* #553 — the admin's memory, in Cloud Run / Kubernetes quantity notation.
|
|
414
|
+
* The admin runs the quality audit (Lighthouse driving the bundled
|
|
415
|
+
* Chromium in a child process) next to the editor's requests; 1 GiB left
|
|
416
|
+
* no headroom for both, 2 GiB does. Every adapter exposes the same
|
|
417
|
+
* `adminMemory` knob with this default (CLAUDE.md §11.B), and `upgrade`
|
|
418
|
+
* raises an existing install's admin to it (never lowers a larger value).
|
|
419
|
+
*/
|
|
420
|
+
export const ADMIN_MEMORY_DEFAULT = "2Gi";
|
|
421
|
+
/**
|
|
422
|
+
* Parse a memory quantity (`512Mi`, `2Gi`, `2G`, `1.5Gi`) to MiB; null when
|
|
423
|
+
* it is not a quantity this contract understands.
|
|
424
|
+
*/
|
|
425
|
+
/**
|
|
426
|
+
* #553 — Azure Container Apps (Consumption) only accepts fixed CPU/memory
|
|
427
|
+
* pairs: 0.25 vCPU / 0.5Gi up to 4 vCPU / 8Gi in 0.25 vCPU steps, memory
|
|
428
|
+
* always 2 GiB per vCPU. Map `adminMemory` onto that pair, or throw with
|
|
429
|
+
* the accepted values — an unsupported size must fail before `pulumi up`
|
|
430
|
+
* does, not deploy something else.
|
|
431
|
+
*/
|
|
432
|
+
export function azureContainerAppResources(quantity) {
|
|
433
|
+
const mib = memoryQuantityMiB(quantity);
|
|
434
|
+
if (mib === null || mib % 512 !== 0 || mib < 512 || mib > 8192) {
|
|
435
|
+
throw new Error(`caelo-azure:adminMemory "${quantity}" is not a Container Apps size; use 0.5Gi to 8Gi in 0.5Gi steps (e.g. 2Gi = 1 vCPU)`);
|
|
436
|
+
}
|
|
437
|
+
return { cpu: mib / 2048, memory: `${(mib / 1024).toFixed(1)}Gi` };
|
|
438
|
+
}
|
|
439
|
+
export function memoryQuantityMiB(quantity) {
|
|
440
|
+
const m = /^(\d+(?:\.\d+)?)\s*(Mi|Gi|M|G)$/.exec(quantity.trim());
|
|
441
|
+
if (!m)
|
|
442
|
+
return null;
|
|
443
|
+
const n = Number(m[1]);
|
|
444
|
+
switch (m[2]) {
|
|
445
|
+
case "Mi":
|
|
446
|
+
return Math.round(n);
|
|
447
|
+
case "Gi":
|
|
448
|
+
return Math.round(n * 1024);
|
|
449
|
+
case "M":
|
|
450
|
+
return Math.round((n * 1000 * 1000) / (1024 * 1024));
|
|
451
|
+
default:
|
|
452
|
+
return Math.round((n * 1000 * 1000 * 1000) / (1024 * 1024));
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
//# sourceMappingURL=stack-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack-contract.js","sourceRoot":"","sources":["../src/stack-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converge an existing GCP install to what its Pulumi stack would produce,
|
|
3
|
+
* through gcloud (`cms-provision upgrade` does not run Pulumi). The desired
|
|
4
|
+
* state comes from stack-contract.ts, which the stacks use too.
|
|
5
|
+
*
|
|
6
|
+
* - {@link planEnvUpdate}: the env-var flags for the `services update` that
|
|
7
|
+
* rolls the image, so env changes ride the same new revision — including
|
|
8
|
+
* moving a plain var to a Secret Manager reference.
|
|
9
|
+
* - {@link ensureStackInvariants}: IAM bindings + CDN settings. Each binding
|
|
10
|
+
* is checked against the resource's policy first and only added when
|
|
11
|
+
* missing, so an operator without IAM-admin rights can still upgrade an
|
|
12
|
+
* install that is already in shape.
|
|
13
|
+
*/
|
|
14
|
+
import { type GcloudResult } from "./gcloud.js";
|
|
15
|
+
import { type GcloudRunner, type Sleep } from "./gcloud-retry.js";
|
|
16
|
+
import { type CloudRunEnvVar, type CloudRunSlug, type GcpProvider, type ServiceEnvInputs } from "./stack-contract.js";
|
|
17
|
+
/** An env var as a deployed Cloud Run service carries it. */
|
|
18
|
+
export type LiveEnvValue = {
|
|
19
|
+
readonly kind: "value";
|
|
20
|
+
readonly value: string;
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: "secret";
|
|
23
|
+
readonly secret: string;
|
|
24
|
+
readonly version: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Env vars of the first container in `gcloud run services describe
|
|
28
|
+
* --format=json` output (Knative `serving.knative.dev/v1` Service).
|
|
29
|
+
*/
|
|
30
|
+
export declare function liveContainerEnv(serviceJson: string): Map<string, LiveEnvValue>;
|
|
31
|
+
/** The service account the service's revisions run as (`undefined`: the default compute SA). */
|
|
32
|
+
export declare function liveServiceAccount(serviceJson: string): string | undefined;
|
|
33
|
+
export interface EnvChange {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
/** `undefined` when the service does not have the var yet. */
|
|
36
|
+
readonly from: string | undefined;
|
|
37
|
+
/** `undefined` when the var is removed. */
|
|
38
|
+
readonly to: string | undefined;
|
|
39
|
+
}
|
|
40
|
+
export type EnvUpdatePlan = {
|
|
41
|
+
readonly ok: true;
|
|
42
|
+
readonly changes: EnvChange[];
|
|
43
|
+
readonly flags: string[];
|
|
44
|
+
} | {
|
|
45
|
+
readonly ok: false;
|
|
46
|
+
readonly error: string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Whether a service still carries a URL with an inline password in a plain
|
|
50
|
+
* var — the pre-Secret Manager shape. Such a password is in every revision
|
|
51
|
+
* Cloud Run keeps, so it should be rotated once the services moved off it.
|
|
52
|
+
*/
|
|
53
|
+
export declare function liveEnvHasInlinePassword(liveEnv: ReadonlyMap<string, LiveEnvValue>): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The `gcloud run services update` flags that bring `live` to `desired`.
|
|
56
|
+
* Only vars that differ are touched; vars the contract does not own
|
|
57
|
+
* (operator additions) are left alone, and the `retired` ones are removed.
|
|
58
|
+
*
|
|
59
|
+
* A plain var that becomes a Secret Manager reference moves in the same
|
|
60
|
+
* update: `--remove-env-vars=X --update-secrets=X=…`. gcloud applies all
|
|
61
|
+
* literal env changes (removals included) before the secret changes of one
|
|
62
|
+
* `services update` (googlecloudsdk/command_lib/run/flags.py
|
|
63
|
+
* `_GetConfigurationChanges`), so the new revision has X as a secret and no
|
|
64
|
+
* revision is ever without X. The reverse — a secret becoming a plain value
|
|
65
|
+
* — cannot be expressed in one update (the literal is set while X is still
|
|
66
|
+
* a secret, which gcloud rejects), so it is refused rather than attempted.
|
|
67
|
+
*/
|
|
68
|
+
export declare function planEnvUpdate(live: ReadonlyMap<string, LiveEnvValue>, desired: readonly CloudRunEnvVar[], retired?: readonly string[]): EnvUpdatePlan;
|
|
69
|
+
/** A deployed Cloud Run service as upgrade found it. */
|
|
70
|
+
export interface DeployedService {
|
|
71
|
+
/** Pulumi-suffixed service name. */
|
|
72
|
+
readonly serviceName: string;
|
|
73
|
+
readonly liveEnv: ReadonlyMap<string, LiveEnvValue>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The Cloud SQL host the admin connects to, from its live `ADMIN_DATABASE_URL`
|
|
77
|
+
* (with or without an inline password — before and after the move to Secret
|
|
78
|
+
* Manager).
|
|
79
|
+
*/
|
|
80
|
+
export declare function liveDatabaseHost(liveEnv: ReadonlyMap<string, LiveEnvValue>): {
|
|
81
|
+
ok: true;
|
|
82
|
+
host: string;
|
|
83
|
+
} | {
|
|
84
|
+
ok: false;
|
|
85
|
+
error: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The env changes that bring both deployed services to their env contract
|
|
89
|
+
* (stack-contract.ts — the contract the stacks deploy). Values Pulumi
|
|
90
|
+
* generated at install time (database host, Firebase site) are read from the
|
|
91
|
+
* live services. Fails instead of guessing when one can't be determined.
|
|
92
|
+
*/
|
|
93
|
+
export declare function planContractEnv(install: ServiceEnvInputs, services: Readonly<Record<CloudRunSlug, DeployedService>>, opts?: {
|
|
94
|
+
/** Contract vars to leave exactly as the services have them. */
|
|
95
|
+
readonly leaveUntouched?: readonly string[];
|
|
96
|
+
}): {
|
|
97
|
+
readonly ok: true;
|
|
98
|
+
readonly services: Record<CloudRunSlug, {
|
|
99
|
+
flags: string[];
|
|
100
|
+
changes: EnvChange[];
|
|
101
|
+
}>;
|
|
102
|
+
} | {
|
|
103
|
+
readonly ok: false;
|
|
104
|
+
readonly error: string;
|
|
105
|
+
};
|
|
106
|
+
/** The deployed install `ensureStackInvariants` works on. */
|
|
107
|
+
export interface InstallTarget {
|
|
108
|
+
readonly provider: GcpProvider;
|
|
109
|
+
readonly projectId: string;
|
|
110
|
+
readonly region: string;
|
|
111
|
+
/** Pulumi stack name (`production`). */
|
|
112
|
+
readonly env: string;
|
|
113
|
+
/** Deployed (Pulumi-suffixed) Cloud Run service names. */
|
|
114
|
+
readonly services: Readonly<Record<CloudRunSlug, string>>;
|
|
115
|
+
}
|
|
116
|
+
export interface InvariantOutcome {
|
|
117
|
+
/** Human-readable identity, e.g. `roles/run.viewer → serviceAccount:… on gateway`. */
|
|
118
|
+
readonly id: string;
|
|
119
|
+
readonly status: "present" | "applied" | "failed";
|
|
120
|
+
/** What a failure means for the upgrade (from the invariant). */
|
|
121
|
+
readonly onFailure: "abort" | "warn";
|
|
122
|
+
readonly why: string;
|
|
123
|
+
readonly error?: string;
|
|
124
|
+
}
|
|
125
|
+
export interface InvariantsReport {
|
|
126
|
+
readonly outcomes: InvariantOutcome[];
|
|
127
|
+
/** True when a failed invariant is one the install cannot work without. */
|
|
128
|
+
readonly mustAbort: boolean;
|
|
129
|
+
}
|
|
130
|
+
/** Whether an IAM policy (JSON) grants `role` to `member` unconditionally. */
|
|
131
|
+
export declare function policyGrants(policyJson: string, role: string, member: string): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Idempotently ensure every IAM binding and CDN setting the install's stack
|
|
134
|
+
* declares (stack-contract.ts). Additive only — nothing is removed. Never
|
|
135
|
+
* throws for a gcloud failure; the report says what failed and whether the
|
|
136
|
+
* upgrade must stop.
|
|
137
|
+
*/
|
|
138
|
+
export declare function ensureStackInvariants(install: InstallTarget, deps?: {
|
|
139
|
+
run?: GcloudRunner;
|
|
140
|
+
sleep?: Sleep;
|
|
141
|
+
}): Promise<InvariantsReport>;
|
|
142
|
+
/** The memory limit of the service's container (`512Mi`, `2Gi`), or null
|
|
143
|
+
* when the service declares none (Cloud Run then runs it with 512Mi). */
|
|
144
|
+
export declare function liveContainerMemory(serviceJson: string): string | null;
|
|
145
|
+
/**
|
|
146
|
+
* #553 — raise the admin's memory to the stack's default (it runs the
|
|
147
|
+
* Lighthouse quality audit). Upgrade only ever RAISES: an operator who set
|
|
148
|
+
* more keeps it, and a value this contract cannot parse is left alone and
|
|
149
|
+
* reported, never overwritten blindly.
|
|
150
|
+
*/
|
|
151
|
+
export declare function planAdminMemory(live: string | null, desired?: string): {
|
|
152
|
+
readonly ok: true;
|
|
153
|
+
readonly flags: readonly string[];
|
|
154
|
+
readonly from: string | null;
|
|
155
|
+
} | {
|
|
156
|
+
readonly ok: false;
|
|
157
|
+
readonly error: string;
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* The single `gcloud run services update` that rolls a service to a new
|
|
161
|
+
* image, its run SA and its env changes, so all land in one new revision.
|
|
162
|
+
*/
|
|
163
|
+
export declare function serviceRollArgs(roll: {
|
|
164
|
+
readonly serviceName: string;
|
|
165
|
+
readonly region: string;
|
|
166
|
+
readonly projectId: string;
|
|
167
|
+
readonly imageRef: string;
|
|
168
|
+
/** The SA the new revision runs as (stack-contract.ts per-service SAs). */
|
|
169
|
+
readonly serviceAccount: string;
|
|
170
|
+
readonly envFlags: readonly string[];
|
|
171
|
+
/** Resource changes (`--memory=…`) that ride the same revision. */
|
|
172
|
+
readonly resourceFlags?: readonly string[];
|
|
173
|
+
}): string[];
|
|
174
|
+
/**
|
|
175
|
+
* Run a roll (`serviceRollArgs`), retrying while it fails only because a
|
|
176
|
+
* fresh secret binding has not propagated yet. Any other failure returns at
|
|
177
|
+
* once.
|
|
178
|
+
*/
|
|
179
|
+
export declare function rollService(args: string[], deps?: {
|
|
180
|
+
run?: GcloudRunner;
|
|
181
|
+
sleep?: Sleep;
|
|
182
|
+
}): Promise<GcloudResult>;
|
|
183
|
+
//# sourceMappingURL=stack-converge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack-converge.d.ts","sourceRoot":"","sources":["../src/stack-converge.ts"],"names":[],"mappings":"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"}
|