@caelo-cms/provisioning 0.10.43 → 0.10.46
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 +1 -1
- package/dist/adc-impersonation.d.ts +28 -0
- package/dist/adc-impersonation.d.ts.map +1 -0
- package/dist/adc-impersonation.js +126 -0
- package/dist/adc-impersonation.js.map +1 -0
- package/dist/caddy.js +1 -1
- package/dist/checkpoint-secrets.d.ts.map +1 -1
- package/dist/checkpoint-secrets.js +2 -1
- package/dist/checkpoint-secrets.js.map +1 -1
- package/dist/cli.js +208 -43
- package/dist/cli.js.map +1 -1
- package/dist/compose.d.ts +45 -0
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +112 -29
- package/dist/compose.js.map +1 -1
- package/dist/gcloud-retry.d.ts +5 -1
- package/dist/gcloud-retry.d.ts.map +1 -1
- package/dist/gcloud-retry.js.map +1 -1
- package/dist/gcloud.d.ts +2 -1
- package/dist/gcloud.d.ts.map +1 -1
- package/dist/gcloud.js +11 -13
- package/dist/gcloud.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/infra-contract.d.ts +24 -0
- package/dist/infra-contract.d.ts.map +1 -0
- package/dist/infra-contract.js +25 -0
- package/dist/infra-contract.js.map +1 -0
- package/dist/install-state.d.ts +68 -2
- package/dist/install-state.d.ts.map +1 -1
- package/dist/install-state.js +166 -22
- package/dist/install-state.js.map +1 -1
- package/dist/lifecycle.d.ts +6 -0
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +185 -819
- package/dist/lifecycle.js.map +1 -1
- package/dist/mcp-iap.js +1 -1
- package/dist/migration-runner.d.ts +3 -0
- package/dist/migration-runner.d.ts.map +1 -1
- package/dist/migration-runner.js +7 -5
- package/dist/migration-runner.js.map +1 -1
- package/dist/ops-backend.d.ts +46 -0
- package/dist/ops-backend.d.ts.map +1 -0
- package/dist/ops-backend.js +370 -0
- package/dist/ops-backend.js.map +1 -0
- package/dist/provisioner-identity.d.ts +103 -0
- package/dist/provisioner-identity.d.ts.map +1 -0
- package/dist/provisioner-identity.js +323 -0
- package/dist/provisioner-identity.js.map +1 -0
- package/dist/regions.d.ts.map +1 -1
- package/dist/regions.js +2 -1
- package/dist/regions.js.map +1 -1
- package/dist/release-contract.d.ts +37 -0
- package/dist/release-contract.d.ts.map +1 -0
- package/dist/release-contract.js +55 -0
- package/dist/release-contract.js.map +1 -0
- package/dist/release-manifest.d.ts +140 -0
- package/dist/release-manifest.d.ts.map +1 -0
- package/dist/release-manifest.js +319 -0
- package/dist/release-manifest.js.map +1 -0
- package/dist/self-command.d.ts +16 -0
- package/dist/self-command.d.ts.map +1 -0
- package/dist/self-command.js +20 -0
- package/dist/self-command.js.map +1 -0
- package/dist/self-hosted-release.d.ts +81 -0
- package/dist/self-hosted-release.d.ts.map +1 -0
- package/dist/self-hosted-release.js +148 -0
- package/dist/self-hosted-release.js.map +1 -0
- package/dist/self-hosted-upgrade.d.ts +91 -0
- package/dist/self-hosted-upgrade.d.ts.map +1 -0
- package/dist/self-hosted-upgrade.js +355 -0
- package/dist/self-hosted-upgrade.js.map +1 -0
- package/dist/stack-contract.js +2 -2
- package/dist/stack-converge.d.ts.map +1 -1
- package/dist/stack-converge.js +2 -1
- package/dist/stack-converge.js.map +1 -1
- package/dist/staging-admin.js +3 -3
- package/dist/state-backend.d.ts +103 -0
- package/dist/state-backend.d.ts.map +1 -0
- package/dist/state-backend.js +180 -0
- package/dist/state-backend.js.map +1 -0
- package/dist/state-store.d.ts +55 -0
- package/dist/state-store.d.ts.map +1 -0
- package/dist/state-store.js +140 -0
- package/dist/state-store.js.map +1 -0
- package/dist/upgrade/app-roll.d.ts +164 -0
- package/dist/upgrade/app-roll.d.ts.map +1 -0
- package/dist/upgrade/app-roll.js +270 -0
- package/dist/upgrade/app-roll.js.map +1 -0
- package/dist/upgrade/backup.d.ts +48 -0
- package/dist/upgrade/backup.d.ts.map +1 -0
- package/dist/upgrade/backup.js +71 -0
- package/dist/upgrade/backup.js.map +1 -0
- package/dist/upgrade/converge.d.ts +45 -0
- package/dist/upgrade/converge.d.ts.map +1 -0
- package/dist/upgrade/converge.js +287 -0
- package/dist/upgrade/converge.js.map +1 -0
- package/dist/upgrade/gcp-lookup.d.ts +23 -0
- package/dist/upgrade/gcp-lookup.d.ts.map +1 -0
- package/dist/upgrade/gcp-lookup.js +95 -0
- package/dist/upgrade/gcp-lookup.js.map +1 -0
- package/dist/upgrade/health.d.ts +99 -0
- package/dist/upgrade/health.d.ts.map +1 -0
- package/dist/upgrade/health.js +214 -0
- package/dist/upgrade/health.js.map +1 -0
- package/dist/upgrade/release-images.d.ts +125 -0
- package/dist/upgrade/release-images.d.ts.map +1 -0
- package/dist/upgrade/release-images.js +315 -0
- package/dist/upgrade/release-images.js.map +1 -0
- package/dist/upgrade/reporter.d.ts +17 -0
- package/dist/upgrade/reporter.d.ts.map +1 -0
- package/dist/upgrade/reporter.js +28 -0
- package/dist/upgrade/reporter.js.map +1 -0
- package/dist/upgrade/upgrade.d.ts +79 -0
- package/dist/upgrade/upgrade.d.ts.map +1 -0
- package/dist/upgrade/upgrade.js +105 -0
- package/dist/upgrade/upgrade.js.map +1 -0
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +7 -3
- package/dist/wizard.js.map +1 -1
- package/dist/wizards/gcp-cost.d.ts.map +1 -1
- package/dist/wizards/gcp-cost.js +7 -1
- package/dist/wizards/gcp-cost.js.map +1 -1
- package/dist/wizards/gcp-pulumi.d.ts +99 -8
- package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
- package/dist/wizards/gcp-pulumi.js +157 -50
- package/dist/wizards/gcp-pulumi.js.map +1 -1
- package/dist/wizards/gcp.d.ts.map +1 -1
- package/dist/wizards/gcp.js +185 -61
- package/dist/wizards/gcp.js.map +1 -1
- package/package.json +4 -3
- package/stacks/aws/README.md +1 -1
- package/stacks/gcp/Pulumi.yaml +1 -1
- package/stacks/gcp/README.md +1 -1
- package/stacks/gcp/index.ts +1 -1
- package/stacks/gcp-firebase/Pulumi.yaml +1 -1
- package/stacks/gcp-firebase/index.ts +2 -2
- package/stacks/self-hosted/README.md +4 -0
- package/stacks/self-hosted/index.ts +10 -2
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Where install.json and progress.json are read from and written to (#696).
|
|
4
|
+
*
|
|
5
|
+
* - {@link LocalStateStore}: `~/.caelo-<id>/` — every install before #696,
|
|
6
|
+
* and self-hosted always.
|
|
7
|
+
* - {@link GcsStateStore}: the install's ops bucket
|
|
8
|
+
* (`gs://<project>-caelo-ops/install/`), which is authoritative. A local
|
|
9
|
+
* copy is kept in `~/.caelo-<id>/` so `listInstalls` and the install
|
|
10
|
+
* picker work offline, but every read in a process starts from the
|
|
11
|
+
* bucket and every write goes to the bucket first.
|
|
12
|
+
*
|
|
13
|
+
* Both stores refuse to write secret material (checkpoint-secrets.ts, #656 /
|
|
14
|
+
* #659): install.json and progress.json are read by tooling, backups and
|
|
15
|
+
* agents, and a bucket copy is no place for a database password either. The
|
|
16
|
+
* guard sits inside each store's `write`, so no caller can skip it.
|
|
17
|
+
*
|
|
18
|
+
* The interface is synchronous on purpose: install-state.ts's callers (the
|
|
19
|
+
* wizard steps, `markStepDone`, the lifecycle commands) are synchronous, and
|
|
20
|
+
* the bucket store shells out to `gcloud storage` synchronously.
|
|
21
|
+
*/
|
|
22
|
+
import { spawnSync } from "node:child_process";
|
|
23
|
+
import { existsSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import { assertNoCheckpointSecrets, assertNoSecrets } from "./checkpoint-secrets.js";
|
|
26
|
+
import { gcloudEnv } from "./gcloud.js";
|
|
27
|
+
import { OPS_INSTALL_PREFIX } from "./state-backend.js";
|
|
28
|
+
/** The real {@link SyncGcloud}: `gcloud` with prompts disabled. */
|
|
29
|
+
export const realSyncGcloud = (args, input) => {
|
|
30
|
+
const r = spawnSync("gcloud", args, {
|
|
31
|
+
env: gcloudEnv(),
|
|
32
|
+
encoding: "utf8",
|
|
33
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
34
|
+
...(input !== undefined ? { input } : { stdio: ["ignore", "pipe", "pipe"] }),
|
|
35
|
+
});
|
|
36
|
+
if (r.error)
|
|
37
|
+
throw r.error;
|
|
38
|
+
return {
|
|
39
|
+
ok: r.status === 0,
|
|
40
|
+
stdout: r.stdout ?? "",
|
|
41
|
+
stderr: r.stderr ?? "",
|
|
42
|
+
exitCode: r.status ?? 1,
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Serialize a document after the checkpoint-secrets guard. install.json is
|
|
47
|
+
* checked for any secret; progress.json with the checkpoint rules (step
|
|
48
|
+
* payloads, outputs).
|
|
49
|
+
*/
|
|
50
|
+
export function serializeGuarded(file, value) {
|
|
51
|
+
if (file === "install.json")
|
|
52
|
+
assertNoSecrets(value, file);
|
|
53
|
+
else
|
|
54
|
+
assertNoCheckpointSecrets(value, file);
|
|
55
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
56
|
+
}
|
|
57
|
+
/** Atomic mode-0600 write (temp file + rename in the same directory). */
|
|
58
|
+
function writeTextAtomic(path, text) {
|
|
59
|
+
const tmp = `${path}.tmp-${process.pid}`;
|
|
60
|
+
try {
|
|
61
|
+
writeFileSync(tmp, text, { mode: 0o600 });
|
|
62
|
+
renameSync(tmp, path);
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
rmSync(tmp, { force: true });
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** install.json + progress.json in `~/.caelo-<id>/`. */
|
|
70
|
+
export class LocalStateStore {
|
|
71
|
+
root;
|
|
72
|
+
kind = "local";
|
|
73
|
+
constructor(root) {
|
|
74
|
+
this.root = root;
|
|
75
|
+
}
|
|
76
|
+
read(file) {
|
|
77
|
+
const path = join(this.root, file);
|
|
78
|
+
return existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
79
|
+
}
|
|
80
|
+
write(file, value) {
|
|
81
|
+
writeTextAtomic(join(this.root, file), serializeGuarded(file, value));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** gcloud's wording for an object that is not there. */
|
|
85
|
+
const NOT_FOUND = /matched no objects|No URLs matched|NotFound|404|does not exist/i;
|
|
86
|
+
/**
|
|
87
|
+
* install.json + progress.json in the ops bucket, mirrored to
|
|
88
|
+
* `~/.caelo-<id>/`. The first read of a document in a process fetches it
|
|
89
|
+
* from the bucket; later reads use the in-memory copy, which every write
|
|
90
|
+
* updates. A bucket error throws — the bucket is the source of truth, so
|
|
91
|
+
* silently using the local mirror could resume from a stale checkpoint.
|
|
92
|
+
*/
|
|
93
|
+
export class GcsStateStore {
|
|
94
|
+
bucket;
|
|
95
|
+
root;
|
|
96
|
+
run;
|
|
97
|
+
kind = "gcs";
|
|
98
|
+
cache = new Map();
|
|
99
|
+
constructor(bucket, root, run = realSyncGcloud) {
|
|
100
|
+
this.bucket = bucket;
|
|
101
|
+
this.root = root;
|
|
102
|
+
this.run = run;
|
|
103
|
+
}
|
|
104
|
+
/** `gs://<bucket>/install/<file>`. */
|
|
105
|
+
objectUrl(file) {
|
|
106
|
+
return `gs://${this.bucket}/${OPS_INSTALL_PREFIX}/${file}`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The document as the bucket holds it right now, without caching it or
|
|
110
|
+
* touching the local mirror — for looking at a bucket this machine may
|
|
111
|
+
* not own (the wizard's "is this project already an install?" check).
|
|
112
|
+
*/
|
|
113
|
+
peek(file) {
|
|
114
|
+
const r = this.run(["storage", "cat", this.objectUrl(file)]);
|
|
115
|
+
if (r.ok)
|
|
116
|
+
return r.stdout;
|
|
117
|
+
if (NOT_FOUND.test(r.stderr))
|
|
118
|
+
return null;
|
|
119
|
+
throw new Error(`Could not read ${this.objectUrl(file)}: ${r.stderr.trim() || `gcloud exited ${r.exitCode}`}. Check that you are signed in (gcloud auth login) as an account with access to the install's ops bucket.`);
|
|
120
|
+
}
|
|
121
|
+
read(file) {
|
|
122
|
+
if (this.cache.has(file))
|
|
123
|
+
return this.cache.get(file) ?? null;
|
|
124
|
+
const text = this.peek(file);
|
|
125
|
+
this.cache.set(file, text);
|
|
126
|
+
if (text !== null)
|
|
127
|
+
writeTextAtomic(join(this.root, file), text);
|
|
128
|
+
return text;
|
|
129
|
+
}
|
|
130
|
+
write(file, value) {
|
|
131
|
+
const text = serializeGuarded(file, value);
|
|
132
|
+
const r = this.run(["storage", "cp", "-", this.objectUrl(file), "--content-type=application/json"], text);
|
|
133
|
+
if (!r.ok) {
|
|
134
|
+
throw new Error(`Could not write ${this.objectUrl(file)}: ${r.stderr.trim() || `gcloud exited ${r.exitCode}`}. Nothing was changed locally; fix access to the ops bucket and re-run.`);
|
|
135
|
+
}
|
|
136
|
+
this.cache.set(file, text);
|
|
137
|
+
writeTextAtomic(join(this.root, file), text);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=state-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-store.js","sourceRoot":"","sources":["../src/state-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app-roll half of an upgrade (epic #696): migrations with the new admin
|
|
3
|
+
* image, then admin and gateway onto their new images with the env contract
|
|
4
|
+
* riding the same revision, traffic onto the new revisions, a deep health
|
|
5
|
+
* check, and — on any failure — traffic back onto the revisions that served
|
|
6
|
+
* before.
|
|
7
|
+
*
|
|
8
|
+
* It never changes IAM, never creates a service account or secret and never
|
|
9
|
+
* touches other cloud resources: everything here works under an identity
|
|
10
|
+
* that may only deploy Cloud Run revisions, run the migration job and take a
|
|
11
|
+
* Cloud SQL backup. That is the identity the in-cloud upgrader job runs as.
|
|
12
|
+
* Whatever needs more lives in converge.ts; `cms-provision upgrade` runs
|
|
13
|
+
* both, and the upgrader job refuses to run while the install needs a
|
|
14
|
+
* converge (`needsConverge`).
|
|
15
|
+
*
|
|
16
|
+
* Converge steps that must sit between app-roll steps — a database role that
|
|
17
|
+
* a migration creates gets its password after migrations and before the
|
|
18
|
+
* gateway rolls — come in through {@link AppRollHooks}. The upgrader job
|
|
19
|
+
* passes none.
|
|
20
|
+
*
|
|
21
|
+
* All gcloud calls go through the injected runner, all output through the
|
|
22
|
+
* injected reporter, so the whole sequence runs against a fake in tests.
|
|
23
|
+
*/
|
|
24
|
+
import type { GcloudRunner, Sleep } from "../gcloud-retry.js";
|
|
25
|
+
import type { ImageDigests } from "../install-state.js";
|
|
26
|
+
import { type EnvChange, type LiveEnvValue, type LiveVolumes } from "../stack-converge.js";
|
|
27
|
+
import { type SqlBackup } from "./backup.js";
|
|
28
|
+
import type { HealthProbe } from "./health.js";
|
|
29
|
+
import type { UpgradeReporter } from "./reporter.js";
|
|
30
|
+
export type RollSlug = "admin" | "gateway";
|
|
31
|
+
/** The roll order: the gateway follows the admin, never the other way round. */
|
|
32
|
+
export declare const ROLL_ORDER: readonly RollSlug[];
|
|
33
|
+
/** The install an app-roll acts on. */
|
|
34
|
+
export interface AppRollInstall {
|
|
35
|
+
readonly provider: "gcp" | "gcp-firebase";
|
|
36
|
+
readonly projectId: string;
|
|
37
|
+
readonly region: string;
|
|
38
|
+
/** Pulumi stack name (`production`). */
|
|
39
|
+
readonly env: string;
|
|
40
|
+
readonly domain: string;
|
|
41
|
+
/** The protected staging host the admin is told about (#650). */
|
|
42
|
+
readonly stagingHost: string;
|
|
43
|
+
}
|
|
44
|
+
/** The release image one service rolls to. */
|
|
45
|
+
export interface ReleaseImage {
|
|
46
|
+
/** Fully qualified, digest-pinned reference (`…/admin@sha256:…`). */
|
|
47
|
+
readonly imageRef: string;
|
|
48
|
+
readonly digest: string;
|
|
49
|
+
}
|
|
50
|
+
/** A service as it runs now, plus the image it rolls to. */
|
|
51
|
+
export interface ServicePlan {
|
|
52
|
+
readonly slug: RollSlug;
|
|
53
|
+
readonly serviceName: string;
|
|
54
|
+
readonly imageRef: string;
|
|
55
|
+
readonly digest: string;
|
|
56
|
+
readonly priorRevision: string;
|
|
57
|
+
readonly liveEnv: ReadonlyMap<string, LiveEnvValue>;
|
|
58
|
+
/** Container memory limit now (null = Cloud Run's default). */
|
|
59
|
+
readonly liveMemory: string | null;
|
|
60
|
+
readonly liveVolumes: LiveVolumes;
|
|
61
|
+
}
|
|
62
|
+
/** A service plan plus the changes its roll applies. */
|
|
63
|
+
export interface RollPlan extends ServicePlan {
|
|
64
|
+
readonly envFlags: readonly string[];
|
|
65
|
+
readonly envChanges: readonly EnvChange[];
|
|
66
|
+
/** #553 — e.g. `--memory=2Gi` when the admin runs below the stack default. */
|
|
67
|
+
readonly resourceFlags: readonly string[];
|
|
68
|
+
/** The admin's media bucket volume when it is (partly) missing; empty otherwise. */
|
|
69
|
+
readonly volumeFlags: readonly string[];
|
|
70
|
+
readonly volumeChanges: readonly string[];
|
|
71
|
+
}
|
|
72
|
+
/** Everything an app-roll will do, computed read-only before anything changes. */
|
|
73
|
+
export interface AppRollPlan {
|
|
74
|
+
readonly install: AppRollInstall;
|
|
75
|
+
/** In {@link ROLL_ORDER}. */
|
|
76
|
+
readonly services: readonly RollPlan[];
|
|
77
|
+
/** Notes for the operator (e.g. a memory limit left as is). */
|
|
78
|
+
readonly warnings: readonly string[];
|
|
79
|
+
}
|
|
80
|
+
export type PlanResult = {
|
|
81
|
+
readonly ok: true;
|
|
82
|
+
readonly plan: AppRollPlan;
|
|
83
|
+
} | {
|
|
84
|
+
readonly ok: false;
|
|
85
|
+
readonly error: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Read both services and plan their rolls: prior revisions (the rollback
|
|
89
|
+
* targets), env-contract changes, the admin's memory and media volume. Makes
|
|
90
|
+
* no change; fails when anything a roll needs is missing.
|
|
91
|
+
*/
|
|
92
|
+
export declare function planAppRoll(install: AppRollInstall, images: Readonly<Record<RollSlug, ReleaseImage>>, run: GcloudRunner): Promise<PlanResult>;
|
|
93
|
+
/**
|
|
94
|
+
* The plan with `names` left exactly as the services have them — for env
|
|
95
|
+
* vars that point at something a converge step could not set up.
|
|
96
|
+
*/
|
|
97
|
+
export declare function leavingEnvUntouched(plan: AppRollPlan, names: readonly string[]): AppRollPlan;
|
|
98
|
+
/** Print what the plan will change, one line per change. */
|
|
99
|
+
export declare function reportPlan(plan: AppRollPlan, reporter: UpgradeReporter): void;
|
|
100
|
+
/** Converge steps that must sit between app-roll steps (none for the upgrader job). */
|
|
101
|
+
export interface AppRollHooks {
|
|
102
|
+
/**
|
|
103
|
+
* After migrations, before any service rolls. Returns the contract env
|
|
104
|
+
* vars the rolls must leave untouched because what they point at could
|
|
105
|
+
* not be set up; `ok: false` aborts the upgrade (nothing rolled yet).
|
|
106
|
+
*/
|
|
107
|
+
afterMigrations?(): Promise<{
|
|
108
|
+
readonly ok: true;
|
|
109
|
+
readonly leaveUntouched: readonly string[];
|
|
110
|
+
} | {
|
|
111
|
+
readonly ok: false;
|
|
112
|
+
}>;
|
|
113
|
+
/** Right before `slug` rolls; `ok: false` aborts and rolls back what rolled. */
|
|
114
|
+
beforeRoll?(slug: RollSlug): Promise<{
|
|
115
|
+
readonly ok: true;
|
|
116
|
+
} | {
|
|
117
|
+
readonly ok: false;
|
|
118
|
+
readonly error: string;
|
|
119
|
+
}>;
|
|
120
|
+
/** After `slug` went back to its prior revision (or its roll failed). */
|
|
121
|
+
afterRollback?(slug: RollSlug): Promise<void>;
|
|
122
|
+
}
|
|
123
|
+
export interface AppRollDeps {
|
|
124
|
+
readonly run: GcloudRunner;
|
|
125
|
+
readonly sleep?: Sleep;
|
|
126
|
+
readonly reporter: UpgradeReporter;
|
|
127
|
+
/** Runs both migration targets with `imageRef` (the NEW admin image). */
|
|
128
|
+
readonly migrate: (imageRef: string) => Promise<{
|
|
129
|
+
readonly ok: boolean;
|
|
130
|
+
readonly error?: string;
|
|
131
|
+
}>;
|
|
132
|
+
/** The deep health check, or null when the operator explicitly skipped it. */
|
|
133
|
+
readonly health: HealthProbe | null;
|
|
134
|
+
/** Persist the digests now serving (install.json, stack config, …). */
|
|
135
|
+
readonly recordDigests: (digests: ImageDigests) => Promise<void>;
|
|
136
|
+
}
|
|
137
|
+
export interface AppRollInput {
|
|
138
|
+
readonly plan: AppRollPlan;
|
|
139
|
+
/**
|
|
140
|
+
* The backup taken before this roll. Required: migrations never run
|
|
141
|
+
* without a backup to go back to.
|
|
142
|
+
*/
|
|
143
|
+
readonly backup: SqlBackup;
|
|
144
|
+
/** The release version rolled to, when known (`0.11.0`; null for a channel tag). */
|
|
145
|
+
readonly targetVersion: string | null;
|
|
146
|
+
/**
|
|
147
|
+
* Whether the release's migrations are contract-type (destructive for the
|
|
148
|
+
* previous release; from the signed release manifest). null = unknown.
|
|
149
|
+
*/
|
|
150
|
+
readonly migrationsContract: boolean | null;
|
|
151
|
+
}
|
|
152
|
+
export type AppRollPhase = "health-baseline" | "migrations" | "converge" | "roll" | "health";
|
|
153
|
+
export type AppRollResult = {
|
|
154
|
+
readonly ok: true;
|
|
155
|
+
readonly digests: ImageDigests;
|
|
156
|
+
} | {
|
|
157
|
+
readonly ok: false;
|
|
158
|
+
readonly phase: AppRollPhase;
|
|
159
|
+
/** Whether migrations ran before the failure (the backup matters then). */
|
|
160
|
+
readonly migrated: boolean;
|
|
161
|
+
};
|
|
162
|
+
/** Run the app-roll. See the file comment for the sequence. */
|
|
163
|
+
export declare function executeAppRoll(input: AppRollInput, hooks: AppRollHooks, deps: AppRollDeps): Promise<AppRollResult>;
|
|
164
|
+
//# sourceMappingURL=app-roll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-roll.d.ts","sourceRoot":"","sources":["../../src/upgrade/app-roll.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
import { gatewayServiceAccountEmail, runServiceAccountEmail } from "../gcp-names.js";
|
|
3
|
+
import { adminMediaVolume } from "../stack-contract.js";
|
|
4
|
+
import { liveContainerEnv, liveContainerMemory, liveVolumes, planAdminMemory, planContractEnv, planMediaVolume, rollService, serviceRollArgs, } from "../stack-converge.js";
|
|
5
|
+
import { restoreCommand } from "./backup.js";
|
|
6
|
+
import { describeServiceJson, findCurrentRevision, findServiceUrl, pinTraffic, resolveGcpResourceName, } from "./gcp-lookup.js";
|
|
7
|
+
/** The roll order: the gateway follows the admin, never the other way round. */
|
|
8
|
+
export const ROLL_ORDER = ["admin", "gateway"];
|
|
9
|
+
/**
|
|
10
|
+
* Read both services and plan their rolls: prior revisions (the rollback
|
|
11
|
+
* targets), env-contract changes, the admin's memory and media volume. Makes
|
|
12
|
+
* no change; fails when anything a roll needs is missing.
|
|
13
|
+
*/
|
|
14
|
+
export async function planAppRoll(install, images, run) {
|
|
15
|
+
const services = [];
|
|
16
|
+
for (const slug of ROLL_ORDER) {
|
|
17
|
+
const prefix = `caelo-${install.env}-${slug}`;
|
|
18
|
+
const serviceName = await resolveGcpResourceName(run, "run-service", prefix, install.projectId, install.region);
|
|
19
|
+
if (!serviceName)
|
|
20
|
+
return { ok: false, error: `Could not find the ${prefix}* Cloud Run service` };
|
|
21
|
+
const priorRevision = await findCurrentRevision(run, install.projectId, install.region, serviceName);
|
|
22
|
+
if (!priorRevision) {
|
|
23
|
+
return {
|
|
24
|
+
ok: false,
|
|
25
|
+
error: `Could not capture the current ${slug} revision — refusing to roll`,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const json = await describeServiceJson(run, install.projectId, install.region, serviceName);
|
|
29
|
+
if (!json) {
|
|
30
|
+
return {
|
|
31
|
+
ok: false,
|
|
32
|
+
error: `Could not read the ${slug} service's configuration — refusing to roll`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
services.push({
|
|
36
|
+
slug,
|
|
37
|
+
serviceName,
|
|
38
|
+
...images[slug],
|
|
39
|
+
priorRevision,
|
|
40
|
+
liveEnv: liveContainerEnv(json),
|
|
41
|
+
liveMemory: liveContainerMemory(json),
|
|
42
|
+
liveVolumes: liveVolumes(json),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
const env = planEnv(install, services, []);
|
|
46
|
+
if (!env.ok)
|
|
47
|
+
return { ok: false, error: `the install's env contract can't be applied: ${env.error}` };
|
|
48
|
+
// Without the media volume every upload is lost on the next revision or
|
|
49
|
+
// scale-to-zero, so a volume the roll cannot add fails the plan.
|
|
50
|
+
const admin = services.find((s) => s.slug === "admin");
|
|
51
|
+
const media = planMediaVolume(admin.liveVolumes, adminMediaVolume(install.projectId, install.env));
|
|
52
|
+
if (!media.ok)
|
|
53
|
+
return { ok: false, error: `the admin's media volume can't be set up: ${media.error}` };
|
|
54
|
+
const warnings = [];
|
|
55
|
+
const rolls = services.map((s) => {
|
|
56
|
+
// #553 — the admin runs the Lighthouse quality audit; raise it to the
|
|
57
|
+
// stack's memory default (never lowered; an unparsable value is kept).
|
|
58
|
+
const memory = s.slug === "admin" ? planAdminMemory(s.liveMemory) : null;
|
|
59
|
+
if (memory && !memory.ok)
|
|
60
|
+
warnings.push(`admin memory left as is: ${memory.error}`);
|
|
61
|
+
return {
|
|
62
|
+
...s,
|
|
63
|
+
envFlags: env.services[s.slug].flags,
|
|
64
|
+
envChanges: env.services[s.slug].changes,
|
|
65
|
+
resourceFlags: memory?.ok ? memory.flags : [],
|
|
66
|
+
volumeFlags: s.slug === "admin" ? media.flags : [],
|
|
67
|
+
volumeChanges: s.slug === "admin" ? media.changes : [],
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
return { ok: true, plan: { install, services: rolls, warnings } };
|
|
71
|
+
}
|
|
72
|
+
function planEnv(install, services, leaveUntouched) {
|
|
73
|
+
const deployed = Object.fromEntries(services.map((s) => [s.slug, { serviceName: s.serviceName, liveEnv: s.liveEnv }]));
|
|
74
|
+
return planContractEnv({
|
|
75
|
+
provider: install.provider,
|
|
76
|
+
projectId: install.projectId,
|
|
77
|
+
env: install.env,
|
|
78
|
+
domain: install.domain,
|
|
79
|
+
region: install.region,
|
|
80
|
+
stagingHost: install.stagingHost,
|
|
81
|
+
}, deployed, { leaveUntouched });
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The plan with `names` left exactly as the services have them — for env
|
|
85
|
+
* vars that point at something a converge step could not set up.
|
|
86
|
+
*/
|
|
87
|
+
export function leavingEnvUntouched(plan, names) {
|
|
88
|
+
if (names.length === 0)
|
|
89
|
+
return plan;
|
|
90
|
+
const env = planEnv(plan.install, plan.services, names);
|
|
91
|
+
if (!env.ok)
|
|
92
|
+
return plan;
|
|
93
|
+
return {
|
|
94
|
+
...plan,
|
|
95
|
+
services: plan.services.map((s) => ({
|
|
96
|
+
...s,
|
|
97
|
+
envFlags: env.services[s.slug].flags,
|
|
98
|
+
envChanges: env.services[s.slug].changes,
|
|
99
|
+
})),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** Print what the plan will change, one line per change. */
|
|
103
|
+
export function reportPlan(plan, reporter) {
|
|
104
|
+
for (const w of plan.warnings)
|
|
105
|
+
reporter.warn(w);
|
|
106
|
+
for (const s of plan.services) {
|
|
107
|
+
for (const c of s.volumeChanges)
|
|
108
|
+
reporter.info(`${s.slug} media: ${c}`);
|
|
109
|
+
for (const f of s.resourceFlags) {
|
|
110
|
+
reporter.info(`${s.slug} resources: ${s.liveMemory ?? "(default)"} → ${f}`);
|
|
111
|
+
}
|
|
112
|
+
for (const c of s.envChanges) {
|
|
113
|
+
reporter.info(`${s.slug} env ${c.name}: ${c.from === undefined ? "(unset)" : c.from} → ${c.to === undefined ? "(removed)" : c.to}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Run the app-roll. See the file comment for the sequence. */
|
|
118
|
+
export async function executeAppRoll(input, hooks, deps) {
|
|
119
|
+
const { reporter: r, run } = deps;
|
|
120
|
+
const { install } = input.plan;
|
|
121
|
+
let plan = input.plan;
|
|
122
|
+
const fail = (phase, migrated) => ({
|
|
123
|
+
ok: false,
|
|
124
|
+
phase,
|
|
125
|
+
migrated,
|
|
126
|
+
});
|
|
127
|
+
// A probe that cannot reach the install now would only fail after the
|
|
128
|
+
// migrations, and roll a healthy release back.
|
|
129
|
+
if (deps.health) {
|
|
130
|
+
const s = r.step("Checking the health check can reach admin + gateway...");
|
|
131
|
+
const base = await deps.health.baseline().catch((e) => ({
|
|
132
|
+
ok: false,
|
|
133
|
+
error: e instanceof Error ? e.message : String(e),
|
|
134
|
+
}));
|
|
135
|
+
if (!base.ok) {
|
|
136
|
+
s.failed(`Health check can't reach the install: ${base.error}`);
|
|
137
|
+
r.warn("No migrations ran and no traffic moved. Fix the access problem, or re-run with --skip-health-check to roll with Cloud Run's readiness check only.");
|
|
138
|
+
return fail("health-baseline", false);
|
|
139
|
+
}
|
|
140
|
+
s.done(`Health check reaches the install (${base.detail})`);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
r.warn("--skip-health-check set — the new revisions get Cloud Run's readiness check only.");
|
|
144
|
+
}
|
|
145
|
+
// Migrations BEFORE any traffic moves, with the NEW admin image: the old
|
|
146
|
+
// image does not carry the release's migration files (v0.9.2).
|
|
147
|
+
const admin = plan.services.find((s) => s.slug === "admin");
|
|
148
|
+
const sMig = r.step("Applying DB migrations (idempotent)...");
|
|
149
|
+
const mig = await deps.migrate(admin.imageRef);
|
|
150
|
+
if (!mig.ok) {
|
|
151
|
+
sMig.failed(`Migrations failed (${mig.error ?? "unknown"}). Aborting upgrade.`);
|
|
152
|
+
r.warn(`No traffic was shifted. Inspect the Cloud Run Job logs:\n gcloud logging read 'resource.type=cloud_run_job AND resource.labels.job_name=~"caelo-migrate-.*"' --project=${install.projectId} --limit=50\n${restoreHint(input, "if a migration left the schema half-applied")}`);
|
|
153
|
+
return fail("migrations", true);
|
|
154
|
+
}
|
|
155
|
+
sMig.done("Migrations applied");
|
|
156
|
+
if (hooks.afterMigrations) {
|
|
157
|
+
const converged = await hooks.afterMigrations();
|
|
158
|
+
if (!converged.ok)
|
|
159
|
+
return fail("converge", true);
|
|
160
|
+
plan = leavingEnvUntouched(plan, converged.leaveUntouched);
|
|
161
|
+
}
|
|
162
|
+
const rolled = [];
|
|
163
|
+
const rollBack = async (alsoCurrent) => {
|
|
164
|
+
const services = alsoCurrent ? [...rolled, alsoCurrent] : rolled;
|
|
165
|
+
if (services.length === 0)
|
|
166
|
+
return;
|
|
167
|
+
r.warn(`Rolling back ${services.length} service(s) to the revisions that served before...`);
|
|
168
|
+
for (const p of [...services].reverse()) {
|
|
169
|
+
const ok = await pinTraffic(run, install.projectId, install.region, p.serviceName, p.priorRevision);
|
|
170
|
+
if (ok)
|
|
171
|
+
r.info(` ${p.slug} traffic restored to ${p.priorRevision}`);
|
|
172
|
+
else
|
|
173
|
+
r.error(` ${p.slug} rollback FAILED — pin ${p.priorRevision} by hand`);
|
|
174
|
+
await hooks.afterRollback?.(p.slug);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
for (const p of plan.services) {
|
|
178
|
+
const s = r.step(`Rolling ${p.slug} → ${p.digest.slice(0, 19)}...`);
|
|
179
|
+
const before = (await hooks.beforeRoll?.(p.slug)) ?? { ok: true };
|
|
180
|
+
if (!before.ok) {
|
|
181
|
+
s.failed(before.error);
|
|
182
|
+
await rollBack();
|
|
183
|
+
return fail("roll", true);
|
|
184
|
+
}
|
|
185
|
+
const upd = await rollService(serviceRollArgs({
|
|
186
|
+
serviceName: p.serviceName,
|
|
187
|
+
region: install.region,
|
|
188
|
+
projectId: install.projectId,
|
|
189
|
+
imageRef: p.imageRef,
|
|
190
|
+
serviceAccount: p.slug === "admin"
|
|
191
|
+
? runServiceAccountEmail(install.projectId, install.env)
|
|
192
|
+
: gatewayServiceAccountEmail(install.projectId, install.env),
|
|
193
|
+
envFlags: p.envFlags,
|
|
194
|
+
resourceFlags: p.resourceFlags,
|
|
195
|
+
volumeFlags: p.volumeFlags,
|
|
196
|
+
}), { run, ...(deps.sleep ? { sleep: deps.sleep } : {}) });
|
|
197
|
+
if (!upd.ok) {
|
|
198
|
+
s.failed(`Failed: ${upd.stderr.trim()}`);
|
|
199
|
+
// The failed update created no serving revision; only undo what the
|
|
200
|
+
// hook changed for it, then the services that rolled before.
|
|
201
|
+
await hooks.afterRollback?.(p.slug);
|
|
202
|
+
await rollBack();
|
|
203
|
+
return fail("roll", true);
|
|
204
|
+
}
|
|
205
|
+
// `update --image` only moves traffic when the service has no explicit
|
|
206
|
+
// traffic config — but a rollback pins traffic to the prior revision,
|
|
207
|
+
// and that pin survives later updates. Moving traffic to latest on
|
|
208
|
+
// every roll is idempotent and un-pins services an earlier failed
|
|
209
|
+
// upgrade left pinned.
|
|
210
|
+
const flip = await run([
|
|
211
|
+
"run",
|
|
212
|
+
"services",
|
|
213
|
+
"update-traffic",
|
|
214
|
+
p.serviceName,
|
|
215
|
+
"--region",
|
|
216
|
+
install.region,
|
|
217
|
+
"--project",
|
|
218
|
+
install.projectId,
|
|
219
|
+
"--to-latest",
|
|
220
|
+
"--quiet",
|
|
221
|
+
]);
|
|
222
|
+
if (!flip.ok) {
|
|
223
|
+
s.failed(`Traffic flip to latest failed: ${flip.stderr.trim()}`);
|
|
224
|
+
await rollBack(p);
|
|
225
|
+
return fail("roll", true);
|
|
226
|
+
}
|
|
227
|
+
if (!(await findServiceUrl(run, install.projectId, install.region, p.serviceName))) {
|
|
228
|
+
s.failed(`Could not resolve the service URL for ${p.slug} — rolling back`);
|
|
229
|
+
await rollBack(p);
|
|
230
|
+
return fail("roll", true);
|
|
231
|
+
}
|
|
232
|
+
s.done(`${p.slug} rolled to ${p.digest.slice(0, 19)}...`);
|
|
233
|
+
rolled.push(p);
|
|
234
|
+
}
|
|
235
|
+
if (deps.health) {
|
|
236
|
+
const s = r.step("Deep health check (database, migrations, version)...");
|
|
237
|
+
// A probe that throws is a failed check: roll back, never leave the
|
|
238
|
+
// new revisions serving unverified.
|
|
239
|
+
const deep = await deps.health.deep({ version: input.targetVersion }).catch((e) => ({
|
|
240
|
+
ok: false,
|
|
241
|
+
error: e instanceof Error ? e.message : String(e),
|
|
242
|
+
}));
|
|
243
|
+
if (!deep.ok) {
|
|
244
|
+
s.failed(`Deep health check failed: ${deep.error}`);
|
|
245
|
+
await rollBack();
|
|
246
|
+
r.warn(restoreHint(input, "if the previous release now fails against the migrated schema"));
|
|
247
|
+
return fail("health", true);
|
|
248
|
+
}
|
|
249
|
+
s.done(`Healthy: ${deep.detail}`);
|
|
250
|
+
}
|
|
251
|
+
const digests = {
|
|
252
|
+
admin: plan.services.find((s) => s.slug === "admin").digest,
|
|
253
|
+
gateway: plan.services.find((s) => s.slug === "gateway").digest,
|
|
254
|
+
};
|
|
255
|
+
await deps.recordDigests(digests);
|
|
256
|
+
return { ok: true, digests };
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* The restore hint after migrations ran and the roll failed. Expand-only
|
|
260
|
+
* migrations leave a schema the previous release still runs on, so restoring
|
|
261
|
+
* is only advised outright for contract-type migrations.
|
|
262
|
+
*/
|
|
263
|
+
function restoreHint(input, when) {
|
|
264
|
+
const cmd = restoreCommand(input.backup);
|
|
265
|
+
if (input.migrationsContract === true) {
|
|
266
|
+
return `This release's migrations are contract-type: the previous release may not work against the migrated schema. Restore the pre-upgrade backup ${input.backup.backupId} (overwrites all data written since):\n ${cmd}`;
|
|
267
|
+
}
|
|
268
|
+
return `Pre-upgrade backup: ${input.backup.backupId}. Restore it only ${when} (overwrites all data written since):\n ${cmd}`;
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=app-roll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-roll.js","sourceRoot":"","sources":["../../src/upgrade/app-roll.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-demand Cloud SQL backups (epic #696): the `backup` lifecycle command and
|
|
3
|
+
* the backup every upgrade takes before its migrations. Both GCP providers
|
|
4
|
+
* (`gcp`, `gcp-firebase`) keep their data in the same `caelo-<env>-pg*` Cloud
|
|
5
|
+
* SQL instance, so one implementation serves both.
|
|
6
|
+
*
|
|
7
|
+
* The backup id is what makes the pre-upgrade backup useful: it lands in the
|
|
8
|
+
* upgrade's output and in the restore hint printed when the upgrade has to
|
|
9
|
+
* roll back after migrations ran.
|
|
10
|
+
*/
|
|
11
|
+
import type { GcloudRunner } from "../gcloud-retry.js";
|
|
12
|
+
/** A finished on-demand backup. */
|
|
13
|
+
export interface SqlBackup {
|
|
14
|
+
readonly projectId: string;
|
|
15
|
+
/** Cloud SQL instance name (Pulumi-suffixed). */
|
|
16
|
+
readonly instance: string;
|
|
17
|
+
/** Cloud SQL backup run id. */
|
|
18
|
+
readonly backupId: string;
|
|
19
|
+
}
|
|
20
|
+
export type SqlBackupResult = {
|
|
21
|
+
readonly ok: true;
|
|
22
|
+
readonly backup: SqlBackup;
|
|
23
|
+
} | {
|
|
24
|
+
readonly ok: false;
|
|
25
|
+
readonly error: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Take an on-demand backup of the install's Cloud SQL instance and return its
|
|
29
|
+
* id. `gcloud sql backups create` waits for the backup to finish but does not
|
|
30
|
+
* print the id in a stable shape, so the backup carries a unique description
|
|
31
|
+
* and is looked up by it afterwards.
|
|
32
|
+
*
|
|
33
|
+
* @param purpose short reason recorded in the backup's description, e.g. "pre-upgrade to 0.11.0"
|
|
34
|
+
*/
|
|
35
|
+
export declare function takeSqlBackup(run: GcloudRunner, install: {
|
|
36
|
+
readonly projectId: string;
|
|
37
|
+
readonly env: string;
|
|
38
|
+
}, purpose: string, now?: () => Date): Promise<SqlBackupResult>;
|
|
39
|
+
/** The command that restores `backup` over its instance (overwrites the current data). */
|
|
40
|
+
export declare function restoreCommand(backup: SqlBackup): string;
|
|
41
|
+
/**
|
|
42
|
+
* What to tell the operator when a pre-upgrade backup failed: why nothing
|
|
43
|
+
* happened, and how to get past it.
|
|
44
|
+
*/
|
|
45
|
+
export declare function backupFailureHelp(install: {
|
|
46
|
+
readonly projectId: string;
|
|
47
|
+
}): string;
|
|
48
|
+
//# sourceMappingURL=backup.d.ts.map
|