@caelo-cms/provisioning 0.10.47 → 0.10.49
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/caddy/Caddyfile.production +1 -1
- package/caddy/Caddyfile.staging +1 -1
- package/dist/agent/apply.d.ts +45 -0
- package/dist/agent/apply.d.ts.map +1 -0
- package/dist/agent/apply.js +83 -0
- package/dist/agent/apply.js.map +1 -0
- package/dist/agent/cli-provenance.d.ts +42 -0
- package/dist/agent/cli-provenance.d.ts.map +1 -0
- package/dist/agent/cli-provenance.js +138 -0
- package/dist/agent/cli-provenance.js.map +1 -0
- package/dist/agent/command.d.ts +79 -0
- package/dist/agent/command.d.ts.map +1 -0
- package/dist/agent/command.js +454 -0
- package/dist/agent/command.js.map +1 -0
- package/dist/agent/config.d.ts +30 -0
- package/dist/agent/config.d.ts.map +1 -0
- package/dist/agent/config.js +60 -0
- package/dist/agent/config.js.map +1 -0
- package/dist/agent/lock.d.ts +13 -0
- package/dist/agent/lock.d.ts.map +1 -0
- package/dist/agent/lock.js +100 -0
- package/dist/agent/lock.js.map +1 -0
- package/dist/agent/queue.d.ts +35 -0
- package/dist/agent/queue.d.ts.map +1 -0
- package/dist/agent/queue.js +50 -0
- package/dist/agent/queue.js.map +1 -0
- package/dist/agent/run.d.ts +97 -0
- package/dist/agent/run.d.ts.map +1 -0
- package/dist/agent/run.js +198 -0
- package/dist/agent/run.js.map +1 -0
- package/dist/agent/systemd.d.ts +55 -0
- package/dist/agent/systemd.d.ts.map +1 -0
- package/dist/agent/systemd.js +171 -0
- package/dist/agent/systemd.js.map +1 -0
- package/dist/agent/units.d.ts +44 -0
- package/dist/agent/units.d.ts.map +1 -0
- package/dist/agent/units.js +127 -0
- package/dist/agent/units.js.map +1 -0
- package/dist/agent-db.js +18402 -0
- package/dist/cli.js +119 -16
- package/dist/cli.js.map +1 -1
- package/dist/cloudsql-iam-user.d.ts +39 -0
- package/dist/cloudsql-iam-user.d.ts.map +1 -0
- package/dist/cloudsql-iam-user.js +119 -0
- package/dist/cloudsql-iam-user.js.map +1 -0
- package/dist/compose.d.ts +16 -0
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +26 -1
- package/dist/compose.js.map +1 -1
- package/dist/dns/index.d.ts +2 -0
- package/dist/dns/index.d.ts.map +1 -1
- package/dist/dns/index.js +3 -2
- package/dist/dns/index.js.map +1 -1
- package/dist/dns/manual.d.ts +5 -0
- package/dist/dns/manual.d.ts.map +1 -1
- package/dist/dns/manual.js +28 -12
- package/dist/dns/manual.js.map +1 -1
- package/dist/gcloud.d.ts +2 -0
- package/dist/gcloud.d.ts.map +1 -1
- package/dist/gcloud.js +1 -1
- package/dist/gcloud.js.map +1 -1
- package/dist/gcp-names.d.ts +15 -0
- package/dist/gcp-names.d.ts.map +1 -1
- package/dist/gcp-names.js +23 -0
- package/dist/gcp-names.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/infra-contract.d.ts +1 -1
- package/dist/infra-contract.js +7 -1
- package/dist/infra-contract.js.map +1 -1
- package/dist/install-job-runner.d.ts +13 -0
- package/dist/install-job-runner.d.ts.map +1 -0
- package/dist/install-job-runner.js +155 -0
- package/dist/install-job-runner.js.map +1 -0
- package/dist/install-job.d.ts +210 -0
- package/dist/install-job.d.ts.map +1 -0
- package/dist/install-job.js +449 -0
- package/dist/install-job.js.map +1 -0
- package/dist/install-state.d.ts +10 -1
- package/dist/install-state.d.ts.map +1 -1
- package/dist/install-state.js +10 -2
- package/dist/install-state.js.map +1 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +19 -4
- package/dist/lifecycle.js.map +1 -1
- package/dist/migration-runner.d.ts +5 -0
- package/dist/migration-runner.d.ts.map +1 -1
- package/dist/migration-runner.js +1 -1
- package/dist/migration-runner.js.map +1 -1
- package/dist/operator-access.d.ts +14 -8
- package/dist/operator-access.d.ts.map +1 -1
- package/dist/operator-access.js +16 -102
- package/dist/operator-access.js.map +1 -1
- package/dist/release-manifest.d.ts +2 -41
- package/dist/release-manifest.d.ts.map +1 -1
- package/dist/release-manifest.js +18 -121
- package/dist/release-manifest.js.map +1 -1
- package/dist/run-mode.d.ts +27 -0
- package/dist/run-mode.d.ts.map +1 -0
- package/dist/run-mode.js +22 -0
- package/dist/run-mode.js.map +1 -0
- package/dist/runtime-secrets.d.ts +19 -0
- package/dist/runtime-secrets.d.ts.map +1 -1
- package/dist/runtime-secrets.js +44 -0
- package/dist/runtime-secrets.js.map +1 -1
- package/dist/self-hosted-roles.d.ts +22 -1
- package/dist/self-hosted-roles.d.ts.map +1 -1
- package/dist/self-hosted-roles.js +81 -12
- package/dist/self-hosted-roles.js.map +1 -1
- package/dist/self-hosted-upgrade.d.ts +17 -0
- package/dist/self-hosted-upgrade.d.ts.map +1 -1
- package/dist/self-hosted-upgrade.js +16 -1
- package/dist/self-hosted-upgrade.js.map +1 -1
- package/dist/stack-contract.d.ts +6 -0
- package/dist/stack-contract.d.ts.map +1 -1
- package/dist/stack-contract.js +10 -1
- package/dist/stack-contract.js.map +1 -1
- package/dist/upgrade/converge.d.ts +6 -0
- package/dist/upgrade/converge.d.ts.map +1 -1
- package/dist/upgrade/converge.js +59 -2
- package/dist/upgrade/converge.js.map +1 -1
- package/dist/upgrade/gcp-lookup.d.ts +6 -0
- package/dist/upgrade/gcp-lookup.d.ts.map +1 -1
- package/dist/upgrade/gcp-lookup.js +25 -0
- package/dist/upgrade/gcp-lookup.js.map +1 -1
- package/dist/upgrade/release-images.d.ts +26 -2
- package/dist/upgrade/release-images.d.ts.map +1 -1
- package/dist/upgrade/release-images.js +35 -2
- package/dist/upgrade/release-images.js.map +1 -1
- package/dist/upgrade/upgrade.d.ts +5 -0
- package/dist/upgrade/upgrade.d.ts.map +1 -1
- package/dist/upgrade/upgrade.js +28 -1
- package/dist/upgrade/upgrade.js.map +1 -1
- package/dist/upgrader-assess.d.ts +85 -0
- package/dist/upgrader-assess.d.ts.map +1 -0
- package/dist/upgrader-assess.js +122 -0
- package/dist/upgrader-assess.js.map +1 -0
- package/dist/upgrader-gcp.d.ts +17 -0
- package/dist/upgrader-gcp.d.ts.map +1 -0
- package/dist/upgrader-gcp.js +87 -0
- package/dist/upgrader-gcp.js.map +1 -0
- package/dist/upgrader-job.d.ts +133 -0
- package/dist/upgrader-job.d.ts.map +1 -0
- package/dist/upgrader-job.js +259 -0
- package/dist/upgrader-job.js.map +1 -0
- package/dist/upgrader-reporter.d.ts +35 -0
- package/dist/upgrader-reporter.d.ts.map +1 -0
- package/dist/upgrader-reporter.js +79 -0
- package/dist/upgrader-reporter.js.map +1 -0
- package/dist/upgrader-setup.d.ts +59 -0
- package/dist/upgrader-setup.d.ts.map +1 -0
- package/dist/upgrader-setup.js +308 -0
- package/dist/upgrader-setup.js.map +1 -0
- package/dist/wizard.d.ts +6 -0
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +32 -4
- package/dist/wizard.js.map +1 -1
- package/dist/wizards/gcp-firebase.d.ts +3 -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-install-job.d.ts +74 -0
- package/dist/wizards/gcp-install-job.d.ts.map +1 -0
- package/dist/wizards/gcp-install-job.js +398 -0
- package/dist/wizards/gcp-install-job.js.map +1 -0
- package/dist/wizards/gcp-prompts.d.ts +3 -0
- package/dist/wizards/gcp-prompts.d.ts.map +1 -0
- package/dist/wizards/gcp-prompts.js +25 -0
- package/dist/wizards/gcp-prompts.js.map +1 -0
- package/dist/wizards/gcp-pulumi.d.ts +4 -2
- package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
- package/dist/wizards/gcp-pulumi.js +1 -1
- package/dist/wizards/gcp-pulumi.js.map +1 -1
- package/dist/wizards/gcp.d.ts +40 -0
- package/dist/wizards/gcp.d.ts.map +1 -1
- package/dist/wizards/gcp.js +223 -59
- package/dist/wizards/gcp.js.map +1 -1
- package/package.json +6 -4
- package/src/adapter.ts +97 -0
- package/src/adc-impersonation.ts +176 -0
- package/src/admin-domain.test.ts +254 -0
- package/src/admin-domain.ts +245 -0
- package/src/admin-memory.test.ts +151 -0
- package/src/agent/apply.test.ts +185 -0
- package/src/agent/apply.ts +124 -0
- package/src/agent/cli-provenance.test.ts +159 -0
- package/src/agent/cli-provenance.ts +191 -0
- package/src/agent/command.test.ts +77 -0
- package/src/agent/command.ts +564 -0
- package/src/agent/config.ts +86 -0
- package/src/agent/lock-config.test.ts +97 -0
- package/src/agent/lock.ts +110 -0
- package/src/agent/queue.test.ts +10 -0
- package/src/agent/queue.ts +91 -0
- package/src/agent/run.test.ts +437 -0
- package/src/agent/run.ts +295 -0
- package/src/agent/systemd.ts +209 -0
- package/src/agent/units.test.ts +223 -0
- package/src/agent/units.ts +163 -0
- package/src/azure-front-door.test.ts +119 -0
- package/src/azure-front-door.ts +56 -0
- package/src/bootstrap-token.ts +20 -0
- package/src/caddy.ts +112 -0
- package/src/cdn-copy.ts +91 -0
- package/src/cert-wait.test.ts +194 -0
- package/src/cert-wait.ts +163 -0
- package/src/checkpoint-secrets.test.ts +380 -0
- package/src/checkpoint-secrets.ts +283 -0
- package/src/cli-routing.test.ts +45 -0
- package/src/cli-routing.ts +43 -0
- package/src/cli.ts +1491 -0
- package/src/cloudsql-iam-user.ts +147 -0
- package/src/compose.ts +334 -0
- package/src/default-firewall.test.ts +411 -0
- package/src/default-firewall.ts +342 -0
- package/src/deployed-release.test.ts +231 -0
- package/src/deployed-release.ts +220 -0
- package/src/dns/cloudflare.ts +182 -0
- package/src/dns/groups.test.ts +76 -0
- package/src/dns/groups.ts +86 -0
- package/src/dns/index.ts +56 -0
- package/src/dns/manual.ts +130 -0
- package/src/dns/types.ts +27 -0
- package/src/gateway-credentials.test.ts +180 -0
- package/src/gateway-credentials.ts +144 -0
- package/src/gcloud-components.test.ts +76 -0
- package/src/gcloud-components.ts +85 -0
- package/src/gcloud-retry.ts +41 -0
- package/src/gcloud-runner.test.ts +61 -0
- package/src/gcloud.test.ts +99 -0
- package/src/gcloud.ts +322 -0
- package/src/gcp-names.ts +135 -0
- package/src/index.test.ts +332 -0
- package/src/index.ts +114 -0
- package/src/infra-contract.ts +32 -0
- package/src/install-job-runner.ts +166 -0
- package/src/install-job.test.ts +566 -0
- package/src/install-job.ts +542 -0
- package/src/install-state.test.ts +239 -0
- package/src/install-state.ts +583 -0
- package/src/internal-token.ts +35 -0
- package/src/lifecycle.ts +900 -0
- package/src/mcp-iap-stacks.test.ts +58 -0
- package/src/mcp-iap.test.ts +149 -0
- package/src/mcp-iap.ts +131 -0
- package/src/media-volume.test.ts +296 -0
- package/src/migration-runner.ts +365 -0
- package/src/operator-access-stacks.test.ts +55 -0
- package/src/operator-access.test.ts +332 -0
- package/src/operator-access.ts +554 -0
- package/src/ops-backend.test.ts +320 -0
- package/src/ops-backend.ts +414 -0
- package/src/provisioner-identity.test.ts +482 -0
- package/src/provisioner-identity.ts +407 -0
- package/src/redirects-emit.ts +175 -0
- package/src/region-data.ts +428 -0
- package/src/regions.test.ts +277 -0
- package/src/regions.ts +370 -0
- package/src/release-contract.ts +57 -0
- package/src/release-manifest.test.ts +300 -0
- package/src/release-manifest.ts +310 -0
- package/src/run-mode.ts +40 -0
- package/src/runtime-secrets.test.ts +627 -0
- package/src/runtime-secrets.ts +754 -0
- package/src/self-command.test.ts +14 -0
- package/src/self-command.ts +21 -0
- package/src/self-hosted-compose.test.ts +177 -0
- package/src/self-hosted-release.ts +240 -0
- package/src/self-hosted-roles.integration.test.ts +161 -0
- package/src/self-hosted-roles.test.ts +124 -0
- package/src/self-hosted-roles.ts +203 -0
- package/src/self-hosted-upgrade.test.ts +394 -0
- package/src/self-hosted-upgrade.ts +548 -0
- package/src/stack-contract-parity.test.ts +199 -0
- package/src/stack-contract.test.ts +391 -0
- package/src/stack-contract.ts +888 -0
- package/src/stack-converge-secrets.test.ts +421 -0
- package/src/stack-converge.test.ts +833 -0
- package/src/stack-converge.ts +909 -0
- package/src/staging-admin.test.ts +246 -0
- package/src/staging-admin.ts +324 -0
- package/src/state-attach.test.ts +211 -0
- package/src/state-attach.ts +132 -0
- package/src/state-backend.test.ts +145 -0
- package/src/state-backend.ts +262 -0
- package/src/state-command.ts +293 -0
- package/src/state-migrate.test.ts +479 -0
- package/src/state-migrate.ts +434 -0
- package/src/state-pulumi.ts +289 -0
- package/src/state-store.test.ts +212 -0
- package/src/state-store.ts +243 -0
- package/src/upgrade/app-roll.ts +486 -0
- package/src/upgrade/backup.ts +109 -0
- package/src/upgrade/converge.ts +437 -0
- package/src/upgrade/gcp-lookup.ts +167 -0
- package/src/upgrade/health.test.ts +279 -0
- package/src/upgrade/health.ts +305 -0
- package/src/upgrade/release-images.test.ts +119 -0
- package/src/upgrade/release-images.ts +393 -0
- package/src/upgrade/reporter.ts +46 -0
- package/src/upgrade/upgrade.test.ts +626 -0
- package/src/upgrade/upgrade.ts +249 -0
- package/src/upgrader-assess.test.ts +306 -0
- package/src/upgrader-assess.ts +250 -0
- package/src/upgrader-gcp.ts +123 -0
- package/src/upgrader-job.test.ts +411 -0
- package/src/upgrader-job.ts +502 -0
- package/src/upgrader-reporter.test.ts +46 -0
- package/src/upgrader-reporter.ts +105 -0
- package/src/upgrader-setup.test.ts +169 -0
- package/src/upgrader-setup.ts +355 -0
- package/src/wizard.ts +425 -0
- package/src/wizards/gcp-cost.ts +188 -0
- package/src/wizards/gcp-firebase.ts +72 -0
- package/src/wizards/gcp-install-job.ts +506 -0
- package/src/wizards/gcp-prompts.ts +26 -0
- package/src/wizards/gcp-pulumi.test.ts +246 -0
- package/src/wizards/gcp-pulumi.ts +516 -0
- package/src/wizards/gcp.ts +1935 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the upgrader job may install the release an update request names
|
|
3
|
+
* (epic #696). Pure: the job fetches the signed manifest and reads the
|
|
4
|
+
* install's own state, this decides.
|
|
5
|
+
*
|
|
6
|
+
* The request row comes from the admin, so it is untrusted input: it only
|
|
7
|
+
* names a version and who asked. Everything that decides — the digests, the
|
|
8
|
+
* minimum version to upgrade from, whether the release changes the
|
|
9
|
+
* infrastructure or carries destructive migrations, whether it is a
|
|
10
|
+
* security release — comes from the manifest Caelo's release workflow
|
|
11
|
+
* signed, and the installed version from install.json in the install's ops
|
|
12
|
+
* bucket (which the admin cannot write). So a compromised admin can at most
|
|
13
|
+
* ask for "the newest signed release" again; it cannot pick an older one,
|
|
14
|
+
* skip a required intermediate release or get an infrastructure change past
|
|
15
|
+
* the Owner.
|
|
16
|
+
*
|
|
17
|
+
* Refusal matrix (table-tested in upgrader-assess.test.ts):
|
|
18
|
+
*
|
|
19
|
+
* | Condition | Status |
|
|
20
|
+
* |-----------------------------------------------------|-------------------|
|
|
21
|
+
* | registry unreachable | failed (retry) |
|
|
22
|
+
* | bad signature / tampered blob / replayed manifest | refused |
|
|
23
|
+
* | manifest too new for this upgrader | needs_cloud_shell |
|
|
24
|
+
* | installed version unknown (never upgraded via CLI) | needs_cloud_shell |
|
|
25
|
+
* | target not newer than installed (downgrade/same) | refused |
|
|
26
|
+
* | installed below the release's minFromVersion | refused |
|
|
27
|
+
* | release raises the infrastructure contract | needs_cloud_shell |
|
|
28
|
+
* | contract (destructive) migrations, automatic run | refused |
|
|
29
|
+
* | contract migrations, Owner's click | proceed + warning |
|
|
30
|
+
* | automatic run the update policy does not allow | refused |
|
|
31
|
+
* | automatic "security" run, signed manifest disagrees | refused |
|
|
32
|
+
*
|
|
33
|
+
* Self-hosted installs (the host agent, #696 Phase 4c) have no separately
|
|
34
|
+
* converged infrastructure: their Compose file, roles and bootstrap script
|
|
35
|
+
* are rendered by the target release's own provisioning CLI in the same run
|
|
36
|
+
* that rolls the images. So for them `installed.infraContractVersion` is
|
|
37
|
+
* null and the infra-contract row never applies; every "needs_cloud_shell"
|
|
38
|
+
* stop names the upgrade command on the server instead of Cloud Shell.
|
|
39
|
+
*/
|
|
40
|
+
import { type AutoUpdateBasis, type PlatformUpdateTrigger, type StoredUpdatePolicy } from "@caelo-cms/shared";
|
|
41
|
+
import { type ReleaseManifest, type ReleaseManifestResult } from "./release-manifest.js";
|
|
42
|
+
/** Machine-readable reason codes (`platform_update_requests.refusal_codes`). */
|
|
43
|
+
export type RefusalCode = "registry" | "signature" | "manifest-unreadable" | "install-version-unknown" | "not-newer" | "min-from-version" | "infra-contract" | "contract-migrations" | "policy";
|
|
44
|
+
export type RequestDecision = {
|
|
45
|
+
readonly kind: "proceed";
|
|
46
|
+
readonly manifest: ReleaseManifest;
|
|
47
|
+
/** Notes for the timeline (e.g. destructive migrations the Owner accepted). */
|
|
48
|
+
readonly warnings: readonly string[];
|
|
49
|
+
} | {
|
|
50
|
+
readonly kind: "stop";
|
|
51
|
+
readonly status: "refused" | "needs_cloud_shell" | "failed";
|
|
52
|
+
readonly codes: readonly RefusalCode[];
|
|
53
|
+
/** Plain words for the Owner. */
|
|
54
|
+
readonly reason: string;
|
|
55
|
+
/** The manifest, when it verified (for the facts the page explains). */
|
|
56
|
+
readonly manifest?: ReleaseManifest;
|
|
57
|
+
};
|
|
58
|
+
/** What the install's own state says it runs. */
|
|
59
|
+
export interface InstalledState {
|
|
60
|
+
/** install.json `releaseVersion` (self-hosted: release.json); null when never recorded. */
|
|
61
|
+
readonly version: string | null;
|
|
62
|
+
/**
|
|
63
|
+
* install.json `infraContractVersion` (0 when never converged); null for
|
|
64
|
+
* an install without separately converged infrastructure (self-hosted).
|
|
65
|
+
*/
|
|
66
|
+
readonly infraContractVersion: number | null;
|
|
67
|
+
}
|
|
68
|
+
/** Where the Owner runs an upgrade by hand when the automatic path cannot. */
|
|
69
|
+
export type ManualUpgradePath = "cloud-shell" | "server";
|
|
70
|
+
/** Decide what the job does with a request. See the file comment for the matrix. */
|
|
71
|
+
export declare function assessUpgradeRequest(input: {
|
|
72
|
+
readonly request: {
|
|
73
|
+
readonly targetVersion: string;
|
|
74
|
+
readonly trigger: PlatformUpdateTrigger;
|
|
75
|
+
/** Why the admin's scheduler queued an automatic request (Phase 5). */
|
|
76
|
+
readonly autoBasis?: AutoUpdateBasis | null;
|
|
77
|
+
};
|
|
78
|
+
readonly manifest: ReleaseManifestResult;
|
|
79
|
+
readonly installed: InstalledState;
|
|
80
|
+
readonly policy: StoredUpdatePolicy;
|
|
81
|
+
readonly now: Date;
|
|
82
|
+
/** Default `cloud-shell` (the GCP upgrader job). */
|
|
83
|
+
readonly manualPath?: ManualUpgradePath;
|
|
84
|
+
}): RequestDecision;
|
|
85
|
+
//# sourceMappingURL=upgrader-assess.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrader-assess.d.ts","sourceRoot":"","sources":["../src/upgrader-assess.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,EACL,KAAK,eAAe,EAEpB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAIL,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC3B,MAAM,uBAAuB,CAAC;AAG/B,gFAAgF;AAChF,MAAM,MAAM,WAAW,GACnB,UAAU,GACV,WAAW,GACX,qBAAqB,GACrB,yBAAyB,GACzB,WAAW,GACX,kBAAkB,GAClB,gBAAgB,GAChB,qBAAqB,GACrB,QAAQ,CAAC;AAEb,MAAM,MAAM,eAAe,GACvB;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC,GACD;IACE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,mBAAmB,GAAG,QAAQ,CAAC;IAC5D,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,iCAAiC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC;CACrC,CAAC;AAEN,iDAAiD;AACjD,MAAM,WAAW,cAAc;IAC7B,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9C;AAED,8EAA8E;AAC9E,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,QAAQ,CAAC;AAsBzD,oFAAoF;AACpF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;QAC/B,QAAQ,CAAC,OAAO,EAAE,qBAAqB,CAAC;QACxC,uEAAuE;QACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;KAC7C,CAAC;IACF,QAAQ,CAAC,QAAQ,EAAE,qBAAqB,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;IACnB,oDAAoD;IACpD,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;CACzC,GAAG,eAAe,CAkHlB"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Whether the upgrader job may install the release an update request names
|
|
4
|
+
* (epic #696). Pure: the job fetches the signed manifest and reads the
|
|
5
|
+
* install's own state, this decides.
|
|
6
|
+
*
|
|
7
|
+
* The request row comes from the admin, so it is untrusted input: it only
|
|
8
|
+
* names a version and who asked. Everything that decides — the digests, the
|
|
9
|
+
* minimum version to upgrade from, whether the release changes the
|
|
10
|
+
* infrastructure or carries destructive migrations, whether it is a
|
|
11
|
+
* security release — comes from the manifest Caelo's release workflow
|
|
12
|
+
* signed, and the installed version from install.json in the install's ops
|
|
13
|
+
* bucket (which the admin cannot write). So a compromised admin can at most
|
|
14
|
+
* ask for "the newest signed release" again; it cannot pick an older one,
|
|
15
|
+
* skip a required intermediate release or get an infrastructure change past
|
|
16
|
+
* the Owner.
|
|
17
|
+
*
|
|
18
|
+
* Refusal matrix (table-tested in upgrader-assess.test.ts):
|
|
19
|
+
*
|
|
20
|
+
* | Condition | Status |
|
|
21
|
+
* |-----------------------------------------------------|-------------------|
|
|
22
|
+
* | registry unreachable | failed (retry) |
|
|
23
|
+
* | bad signature / tampered blob / replayed manifest | refused |
|
|
24
|
+
* | manifest too new for this upgrader | needs_cloud_shell |
|
|
25
|
+
* | installed version unknown (never upgraded via CLI) | needs_cloud_shell |
|
|
26
|
+
* | target not newer than installed (downgrade/same) | refused |
|
|
27
|
+
* | installed below the release's minFromVersion | refused |
|
|
28
|
+
* | release raises the infrastructure contract | needs_cloud_shell |
|
|
29
|
+
* | contract (destructive) migrations, automatic run | refused |
|
|
30
|
+
* | contract migrations, Owner's click | proceed + warning |
|
|
31
|
+
* | automatic run the update policy does not allow | refused |
|
|
32
|
+
* | automatic "security" run, signed manifest disagrees | refused |
|
|
33
|
+
*
|
|
34
|
+
* Self-hosted installs (the host agent, #696 Phase 4c) have no separately
|
|
35
|
+
* converged infrastructure: their Compose file, roles and bootstrap script
|
|
36
|
+
* are rendered by the target release's own provisioning CLI in the same run
|
|
37
|
+
* that rolls the images. So for them `installed.infraContractVersion` is
|
|
38
|
+
* null and the infra-contract row never applies; every "needs_cloud_shell"
|
|
39
|
+
* stop names the upgrade command on the server instead of Cloud Shell.
|
|
40
|
+
*/
|
|
41
|
+
import { autoInstallRefusal, updateKind, } from "@caelo-cms/shared";
|
|
42
|
+
import { assessAutoInstall, channelForVersion, compareSemver, } from "./release-manifest.js";
|
|
43
|
+
import { needsConverge } from "./upgrade/converge.js";
|
|
44
|
+
/** "…once with Cloud Shell" / "…once on the server": the hand-run upgrade, in words. */
|
|
45
|
+
function manualRun(path) {
|
|
46
|
+
return path === "server"
|
|
47
|
+
? "with the upgrade command on the server (Settings → Updates shows it)"
|
|
48
|
+
: "with Cloud Shell";
|
|
49
|
+
}
|
|
50
|
+
const stop = (status, code, reason, manifest) => ({
|
|
51
|
+
kind: "stop",
|
|
52
|
+
status,
|
|
53
|
+
codes: [code],
|
|
54
|
+
reason,
|
|
55
|
+
...(manifest ? { manifest } : {}),
|
|
56
|
+
});
|
|
57
|
+
/** Decide what the job does with a request. See the file comment for the matrix. */
|
|
58
|
+
export function assessUpgradeRequest(input) {
|
|
59
|
+
const { request, installed } = input;
|
|
60
|
+
const by = manualRun(input.manualPath ?? "cloud-shell");
|
|
61
|
+
const target = request.targetVersion;
|
|
62
|
+
if (!input.manifest.ok) {
|
|
63
|
+
const e = input.manifest.error;
|
|
64
|
+
switch (e.code) {
|
|
65
|
+
case "registry":
|
|
66
|
+
return stop("failed", "registry", `Could not reach the Caelo release registry to verify v${target} (${e.message}). Nothing was changed; try again later.`);
|
|
67
|
+
case "schema":
|
|
68
|
+
return stop("needs_cloud_shell", "manifest-unreadable", `This upgrader is too old to read the release information of v${target}. Install it once ${by}; afterwards the admin can install updates again. (${e.message})`);
|
|
69
|
+
default:
|
|
70
|
+
return stop("refused", "signature", `The release information of v${target} is not signed by Caelo's release workflow, so it was not installed. Nothing was changed. (${e.message})`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const manifest = input.manifest.manifest;
|
|
74
|
+
if (installed.version === null) {
|
|
75
|
+
return stop("needs_cloud_shell", "install-version-unknown", `This install has not recorded which Caelo release it runs yet. Upgrade it once ${by}; that records it, and the admin can install updates from then on.`, manifest);
|
|
76
|
+
}
|
|
77
|
+
if (compareSemver(target, installed.version) <= 0) {
|
|
78
|
+
return stop("refused", "not-newer", `v${target} is not newer than the installed v${installed.version}; Caelo never installs an older release over a newer one.`, manifest);
|
|
79
|
+
}
|
|
80
|
+
if (compareSemver(installed.version, manifest.minFromVersion) < 0) {
|
|
81
|
+
return stop("refused", "min-from-version", `v${target} can only be installed over v${manifest.minFromVersion} or newer; this install runs v${installed.version}. Install v${manifest.minFromVersion} first.`, manifest);
|
|
82
|
+
}
|
|
83
|
+
// Self-hosted: the target's own CLI renders the host setup in this run.
|
|
84
|
+
const infraContractVersion = installed.infraContractVersion ?? manifest.infraContractVersion;
|
|
85
|
+
if (installed.infraContractVersion !== null &&
|
|
86
|
+
needsConverge({ infraContractVersion }, manifest.infraContractVersion)) {
|
|
87
|
+
return stop("needs_cloud_shell", "infra-contract", `v${target} changes this install's cloud setup (infrastructure contract ${infraContractVersion} → ${manifest.infraContractVersion}). The admin may not change cloud permissions, so this release is installed once with your own Google account in Cloud Shell.`, manifest);
|
|
88
|
+
}
|
|
89
|
+
const assessment = assessAutoInstall(manifest, {
|
|
90
|
+
version: installed.version,
|
|
91
|
+
infraContractVersion,
|
|
92
|
+
});
|
|
93
|
+
const destructive = assessment.mode === "manual";
|
|
94
|
+
const warnings = [];
|
|
95
|
+
if (destructive) {
|
|
96
|
+
if (request.trigger === "auto") {
|
|
97
|
+
return stop("refused", "contract-migrations", `v${target} makes database changes the previous release cannot run on (${assessment.reasons.join("; ")}), so it never installs automatically. Install it from Settings → Updates.`, manifest);
|
|
98
|
+
}
|
|
99
|
+
warnings.push(`This release makes database changes the previous release cannot run on (${assessment.reasons.join("; ")}). If it has to roll back, the pre-upgrade backup must be restored.`);
|
|
100
|
+
}
|
|
101
|
+
if (request.trigger === "auto") {
|
|
102
|
+
const kind = updateKind(installed.version, target);
|
|
103
|
+
const refusal = kind === null
|
|
104
|
+
? "the release is not a newer version"
|
|
105
|
+
: autoInstallRefusal(input.policy, {
|
|
106
|
+
kind,
|
|
107
|
+
security: assessment.kind === "security",
|
|
108
|
+
contractMigrations: destructive,
|
|
109
|
+
channel: channelForVersion(target) ?? "unknown",
|
|
110
|
+
}, input.now);
|
|
111
|
+
if (refusal) {
|
|
112
|
+
return stop("refused", "policy", `Not installed automatically: ${refusal}.`, manifest);
|
|
113
|
+
}
|
|
114
|
+
// The scheduler read the manifest unverified; it may only have used the
|
|
115
|
+
// security fix's shorter soak period if the SIGNED manifest backs it.
|
|
116
|
+
if (request.autoBasis === "security" && assessment.kind !== "security") {
|
|
117
|
+
return stop("refused", "policy", `Not installed automatically: v${target} was queued as a security fix, but its signed release information names no published security advisory. Install it from Settings → Updates.`, manifest);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { kind: "proceed", manifest, warnings };
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=upgrader-assess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrader-assess.js","sourceRoot":"","sources":["../src/upgrader-assess.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type GcloudRunner } from "./gcloud-retry.js";
|
|
2
|
+
import type { UpdateQueue, UpgraderJobDeps } from "./upgrader-job.js";
|
|
3
|
+
import type { JobLog } from "./upgrader-reporter.js";
|
|
4
|
+
/** Where the job runs, from the metadata server. */
|
|
5
|
+
export interface UpgraderPlacement {
|
|
6
|
+
readonly projectId: string;
|
|
7
|
+
readonly region: string;
|
|
8
|
+
readonly execution: string | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The Cloud SQL private IP the admin connects to — the job connects there
|
|
12
|
+
* too (as its own IAM database user). Read from the deployed admin service.
|
|
13
|
+
*/
|
|
14
|
+
export declare function upgraderDatabaseHost(placement: Pick<UpgraderPlacement, "projectId" | "region">, run?: GcloudRunner): Promise<string>;
|
|
15
|
+
/** Every dependency of {@link runUpgraderJob} except the database and the log. */
|
|
16
|
+
export declare function gcpUpgraderDeps(placement: UpgraderPlacement, queue: UpdateQueue, log: JobLog): UpgraderJobDeps;
|
|
17
|
+
//# sourceMappingURL=upgrader-gcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrader-gcp.d.ts","sourceRoot":"","sources":["../src/upgrader-gcp.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,YAAY,EAAa,MAAM,mBAAmB,CAAC;AAWjE,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAErD,oDAAoD;AACpD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,QAAQ,CAAC,EAC1D,GAAG,GAAE,YAAqB,GACzB,OAAO,CAAC,MAAM,CAAC,CAQjB;AA4BD,kFAAkF;AAClF,wBAAgB,eAAe,CAC7B,SAAS,EAAE,iBAAiB,EAC5B,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,GACV,eAAe,CAkCjB"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
/**
|
|
3
|
+
* The real side effects of the upgrader job on Google Cloud (epic #696):
|
|
4
|
+
* gcloud as the job's own service account, the install's ops bucket for
|
|
5
|
+
* install.json, the fixed release registry, the migration job and the IAP
|
|
6
|
+
* health probe. upgrader-job.ts takes them as {@link UpgraderJobDeps}; the
|
|
7
|
+
* database queue and the job log come from `@caelo-cms/platform-updater`,
|
|
8
|
+
* which owns the Query API connection.
|
|
9
|
+
*
|
|
10
|
+
* Nothing here reads the process env or argv: what the job may install and
|
|
11
|
+
* from where is compiled in (release-manifest.ts's registry + signer), and
|
|
12
|
+
* where it runs comes from the metadata server.
|
|
13
|
+
*/
|
|
14
|
+
import { mkdtempSync } from "node:fs";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { gcloud } from "./gcloud.js";
|
|
18
|
+
import { realSleep } from "./gcloud-retry.js";
|
|
19
|
+
import { GCP_STACK_ENV } from "./gcp-names.js";
|
|
20
|
+
import { readAdminConfig, runMigrationsViaCloudRunJob } from "./migration-runner.js";
|
|
21
|
+
import { fetchVerifiedReleaseManifest } from "./release-manifest.js";
|
|
22
|
+
import { opsBucketName } from "./state-backend.js";
|
|
23
|
+
import { acquireBucketStateLock, GcsStateStore } from "./state-store.js";
|
|
24
|
+
import { findServiceUrl } from "./upgrade/gcp-lookup.js";
|
|
25
|
+
import { createIapHealthProbe } from "./upgrade/health.js";
|
|
26
|
+
/**
|
|
27
|
+
* The Cloud SQL private IP the admin connects to — the job connects there
|
|
28
|
+
* too (as its own IAM database user). Read from the deployed admin service.
|
|
29
|
+
*/
|
|
30
|
+
export async function upgraderDatabaseHost(placement, run = gcloud) {
|
|
31
|
+
const cfg = await readAdminConfig(placement, run);
|
|
32
|
+
if (!cfg) {
|
|
33
|
+
throw new Error(`could not read the admin service's database host in ${placement.projectId}/${placement.region}`);
|
|
34
|
+
}
|
|
35
|
+
return cfg.databaseHost;
|
|
36
|
+
}
|
|
37
|
+
/** The IAP-backed deep health probe for `plan`'s install. */
|
|
38
|
+
async function healthProbeFor(run, http, plan) {
|
|
39
|
+
const { install } = plan;
|
|
40
|
+
const urls = {};
|
|
41
|
+
for (const s of plan.services) {
|
|
42
|
+
const url = await findServiceUrl(run, install.projectId, install.region, s.serviceName);
|
|
43
|
+
if (!url) {
|
|
44
|
+
return { ok: false, error: `could not read the ${s.slug} service's URL` };
|
|
45
|
+
}
|
|
46
|
+
urls[s.slug] = url;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
ok: true,
|
|
50
|
+
probe: createIapHealthProbe({
|
|
51
|
+
provider: install.provider,
|
|
52
|
+
projectId: install.projectId,
|
|
53
|
+
domain: install.domain,
|
|
54
|
+
adminServiceUrl: urls.admin,
|
|
55
|
+
gatewayServiceUrl: urls.gateway,
|
|
56
|
+
}, { run, http, sleep: realSleep }),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Every dependency of {@link runUpgraderJob} except the database and the log. */
|
|
60
|
+
export function gcpUpgraderDeps(placement, queue, log) {
|
|
61
|
+
const run = gcloud;
|
|
62
|
+
const http = fetch;
|
|
63
|
+
// The bucket store mirrors to a local dir; in the job that is scratch.
|
|
64
|
+
const store = new GcsStateStore(opsBucketName(placement.projectId), mkdtempSync(join(tmpdir(), "caelo-upgrader-")));
|
|
65
|
+
return {
|
|
66
|
+
queue,
|
|
67
|
+
run,
|
|
68
|
+
sleep: realSleep,
|
|
69
|
+
projectId: placement.projectId,
|
|
70
|
+
region: placement.region,
|
|
71
|
+
env: GCP_STACK_ENV,
|
|
72
|
+
execution: placement.execution,
|
|
73
|
+
// The default deps: the fixed registry and signer, never overridden.
|
|
74
|
+
fetchManifest: (version) => fetchVerifiedReleaseManifest(version),
|
|
75
|
+
lockState: () => acquireBucketStateLock(opsBucketName(placement.projectId), "caelo upgrader job"),
|
|
76
|
+
readInstall: () => {
|
|
77
|
+
const text = store.read("install.json");
|
|
78
|
+
return text === null ? null : JSON.parse(text);
|
|
79
|
+
},
|
|
80
|
+
writeInstall: (meta) => store.write("install.json", meta),
|
|
81
|
+
migrate: (imageRef) => runMigrationsViaCloudRunJob({ projectId: placement.projectId, region: placement.region, imageOverride: imageRef }, { run }),
|
|
82
|
+
healthProbe: (plan) => healthProbeFor(run, http, plan),
|
|
83
|
+
now: () => new Date(),
|
|
84
|
+
log,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=upgrader-gcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrader-gcp.js","sourceRoot":"","sources":["../src/upgrader-gcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The upgrader job (epic #696 Phase 2): installs one signed Caelo release
|
|
3
|
+
* on a `gcp` / `gcp-firebase` install, from inside the install's own cloud.
|
|
4
|
+
*
|
|
5
|
+
* The admin's "Install update" writes a `platform_update_requests` row and
|
|
6
|
+
* starts this job. The admin holds only `run.jobs.run` on it — not
|
|
7
|
+
* `runWithOverrides` — so it cannot pass arguments, env or another image
|
|
8
|
+
* (upgrader-setup.ts). The job runs as its own service account, which may
|
|
9
|
+
* deploy Cloud Run revisions, run the migration job and back up Cloud SQL,
|
|
10
|
+
* but cannot change IAM, service accounts or secrets. One run:
|
|
11
|
+
*
|
|
12
|
+
* 1. claim the oldest pending request (ending a run that died mid-way);
|
|
13
|
+
* 2. read what the install runs from install.json in its ops bucket;
|
|
14
|
+
* 3. fetch the release manifest from the FIXED registry and verify its
|
|
15
|
+
* signature (release-manifest.ts) — no registry, identity or digest
|
|
16
|
+
* ever comes from the request, the env or argv;
|
|
17
|
+
* 4. decide (upgrader-assess.ts): refuse a bad signature, a downgrade, a
|
|
18
|
+
* release below its minFromVersion, an automatic run the policy does
|
|
19
|
+
* not allow; hand an infrastructure change to Cloud Shell;
|
|
20
|
+
* 5. take a Cloud SQL backup — no backup, no change;
|
|
21
|
+
* 6. app-roll (upgrade/app-roll.ts) with no converge hooks: migrations
|
|
22
|
+
* with the new admin image, admin then gateway, deep health check,
|
|
23
|
+
* traffic back onto the previous revisions on any failure;
|
|
24
|
+
* 7. record the new digests + version in install.json;
|
|
25
|
+
* 8. move this job onto the release's own ops image, last.
|
|
26
|
+
*
|
|
27
|
+
* Every step is narrated into `platform_update_events` (upgrader-reporter.ts).
|
|
28
|
+
* All side effects come in through {@link UpgraderJobDeps}, so the whole
|
|
29
|
+
* run is tested against fakes; the real wiring is the
|
|
30
|
+
* `@caelo-cms/platform-updater` package (`caelo-ops upgrader`).
|
|
31
|
+
*/
|
|
32
|
+
import type { AutoUpdateBasis, PlatformUpdateEventLevel, PlatformUpdatePhase, PlatformUpdateTrigger, StoredUpdatePolicy } from "@caelo-cms/shared";
|
|
33
|
+
import type { GcloudRunner, Sleep } from "./gcloud-retry.js";
|
|
34
|
+
import type { InstallMetadata } from "./install-state.js";
|
|
35
|
+
import type { ReleaseManifestResult } from "./release-manifest.js";
|
|
36
|
+
import { type AppRollPlan } from "./upgrade/app-roll.js";
|
|
37
|
+
import type { HealthProbe } from "./upgrade/health.js";
|
|
38
|
+
import { type JobLog } from "./upgrader-reporter.js";
|
|
39
|
+
/** A request the job claimed, with the policy as it stood at claim time. */
|
|
40
|
+
export interface ClaimedUpdate {
|
|
41
|
+
readonly id: string;
|
|
42
|
+
readonly targetVersion: string;
|
|
43
|
+
readonly trigger: PlatformUpdateTrigger;
|
|
44
|
+
/** Why the admin's scheduler queued an automatic request; null for the Owner's. */
|
|
45
|
+
readonly autoBasis?: AutoUpdateBasis | null;
|
|
46
|
+
readonly policy: StoredUpdatePolicy;
|
|
47
|
+
}
|
|
48
|
+
/** Facts the job learns and stores on the request for the admin to show. */
|
|
49
|
+
export interface UpdateFacts {
|
|
50
|
+
readonly manifestSecurity?: boolean;
|
|
51
|
+
readonly contractMigrations?: boolean;
|
|
52
|
+
readonly infraContractRequired?: number;
|
|
53
|
+
readonly infraContractInstalled?: number;
|
|
54
|
+
readonly adminDigest?: string;
|
|
55
|
+
readonly gatewayDigest?: string;
|
|
56
|
+
readonly opsDigest?: string;
|
|
57
|
+
readonly backupId?: string;
|
|
58
|
+
}
|
|
59
|
+
/** How a request ended. */
|
|
60
|
+
export type UpdateEndStatus = "succeeded" | "rolled_back" | "failed" | "refused" | "needs_cloud_shell";
|
|
61
|
+
export interface UpdateEnd {
|
|
62
|
+
readonly status: UpdateEndStatus;
|
|
63
|
+
readonly reason: string | null;
|
|
64
|
+
readonly codes: readonly string[];
|
|
65
|
+
/** A failed automatic install pauses automatic installs (Phase 5). */
|
|
66
|
+
readonly pauseAutoUpdates: boolean;
|
|
67
|
+
}
|
|
68
|
+
/** The job's view of the database (the platform_updater role, via the Query API). */
|
|
69
|
+
export interface UpdateQueue {
|
|
70
|
+
/** End a run that died mid-way, then claim the oldest pending request. */
|
|
71
|
+
claim(execution: string | null): Promise<ClaimedUpdate | null>;
|
|
72
|
+
event(requestId: string, phase: PlatformUpdatePhase, level: PlatformUpdateEventLevel, message: string): Promise<void>;
|
|
73
|
+
record(requestId: string, facts: UpdateFacts): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* End a running request. Resolves false when it was no longer running
|
|
76
|
+
* (another process already ended it), so nothing changed.
|
|
77
|
+
*/
|
|
78
|
+
finish(requestId: string, end: UpdateEnd): Promise<boolean>;
|
|
79
|
+
}
|
|
80
|
+
export interface UpgraderJobDeps {
|
|
81
|
+
readonly queue: UpdateQueue;
|
|
82
|
+
readonly run: GcloudRunner;
|
|
83
|
+
readonly sleep?: Sleep;
|
|
84
|
+
/** From the metadata server — never from env or argv. */
|
|
85
|
+
readonly projectId: string;
|
|
86
|
+
readonly region: string;
|
|
87
|
+
/** Pulumi stack name (`production`). */
|
|
88
|
+
readonly env: string;
|
|
89
|
+
/** This Cloud Run execution, for the log link; null outside Cloud Run. */
|
|
90
|
+
readonly execution: string | null;
|
|
91
|
+
/** fetchVerifiedReleaseManifest against the fixed registry. */
|
|
92
|
+
readonly fetchManifest: (version: string) => Promise<ReleaseManifestResult>;
|
|
93
|
+
/**
|
|
94
|
+
* Take the install's state lock (the one `pulumi up` and `state migrate`
|
|
95
|
+
* hold) for the whole run; returns its release. Throws when another run
|
|
96
|
+
* holds it.
|
|
97
|
+
*/
|
|
98
|
+
readonly lockState: () => () => void;
|
|
99
|
+
/** install.json from the install's ops bucket (null when absent). */
|
|
100
|
+
readonly readInstall: () => InstallMetadata | null;
|
|
101
|
+
readonly writeInstall: (meta: InstallMetadata) => void;
|
|
102
|
+
/** Runs both migration targets with the NEW admin image. */
|
|
103
|
+
readonly migrate: (imageRef: string) => Promise<{
|
|
104
|
+
readonly ok: boolean;
|
|
105
|
+
readonly error?: string;
|
|
106
|
+
}>;
|
|
107
|
+
readonly healthProbe: (plan: AppRollPlan) => Promise<{
|
|
108
|
+
readonly ok: true;
|
|
109
|
+
readonly probe: HealthProbe;
|
|
110
|
+
} | {
|
|
111
|
+
readonly ok: false;
|
|
112
|
+
readonly error: string;
|
|
113
|
+
}>;
|
|
114
|
+
readonly now: () => Date;
|
|
115
|
+
readonly log: JobLog;
|
|
116
|
+
}
|
|
117
|
+
/** What one run did: the exit code, and the request it handled (if any). */
|
|
118
|
+
export interface UpgraderRunResult {
|
|
119
|
+
readonly exitCode: number;
|
|
120
|
+
readonly requestId: string | null;
|
|
121
|
+
readonly status: UpdateEndStatus | null;
|
|
122
|
+
}
|
|
123
|
+
/** Run the job once. Never throws; see the file comment for the sequence. */
|
|
124
|
+
export declare function runUpgraderJob(deps: UpgraderJobDeps): Promise<UpgraderRunResult>;
|
|
125
|
+
/**
|
|
126
|
+
* End request `requestId` as `end`, retrying a failed write: the end state
|
|
127
|
+
* is what unblocks the next update, so a transient database error must not
|
|
128
|
+
* leave the request "running" while the run reports success.
|
|
129
|
+
*
|
|
130
|
+
* @returns whether the end was written (false after the last attempt failed)
|
|
131
|
+
*/
|
|
132
|
+
export declare function recordUpdateEnd(queue: UpdateQueue, requestId: string, end: UpdateEnd, log: JobLog, sleep?: Sleep): Promise<boolean>;
|
|
133
|
+
//# sourceMappingURL=upgrader-job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrader-job.d.ts","sourceRoot":"","sources":["../src/upgrader-job.ts"],"names":[],"mappings":"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"}
|