@caelo-cms/provisioning 0.10.55 → 0.10.58
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/agent-db.js +97 -24
- package/dist/cli-routing.js +1 -1
- package/dist/cli-routing.js.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/compose.js +5 -0
- package/dist/compose.js.map +1 -1
- package/dist/converge-checkpoint.d.ts +66 -0
- package/dist/converge-checkpoint.d.ts.map +1 -0
- package/dist/converge-checkpoint.js +61 -0
- package/dist/converge-checkpoint.js.map +1 -0
- package/dist/converge-job-gcp.d.ts +46 -0
- package/dist/converge-job-gcp.d.ts.map +1 -0
- package/dist/converge-job-gcp.js +368 -0
- package/dist/converge-job-gcp.js.map +1 -0
- package/dist/converge-job-setup.d.ts +62 -0
- package/dist/converge-job-setup.d.ts.map +1 -0
- package/dist/converge-job-setup.js +228 -0
- package/dist/converge-job-setup.js.map +1 -0
- package/dist/converge-job.d.ts +141 -0
- package/dist/converge-job.d.ts.map +1 -0
- package/dist/converge-job.js +325 -0
- package/dist/converge-job.js.map +1 -0
- package/dist/gcp-names.d.ts +9 -0
- package/dist/gcp-names.d.ts.map +1 -1
- package/dist/gcp-names.js +13 -0
- package/dist/gcp-names.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/infra-contract.d.ts +1 -1
- package/dist/infra-contract.d.ts.map +1 -1
- package/dist/infra-contract.js +33 -1
- package/dist/infra-contract.js.map +1 -1
- package/dist/install-job.d.ts +11 -3
- package/dist/install-job.d.ts.map +1 -1
- package/dist/install-job.js +22 -12
- package/dist/install-job.js.map +1 -1
- package/dist/lifecycle.d.ts +13 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +115 -58
- package/dist/lifecycle.js.map +1 -1
- package/dist/operator-access.d.ts +4 -0
- package/dist/operator-access.d.ts.map +1 -1
- package/dist/operator-access.js +43 -7
- package/dist/operator-access.js.map +1 -1
- package/dist/release-contract.d.ts +3 -2
- package/dist/release-contract.d.ts.map +1 -1
- package/dist/release-contract.js +5 -2
- package/dist/release-contract.js.map +1 -1
- package/dist/release-manifest.d.ts +2 -0
- package/dist/release-manifest.d.ts.map +1 -1
- package/dist/release-manifest.js.map +1 -1
- package/dist/remote-mcp-edge.d.ts +114 -0
- package/dist/remote-mcp-edge.d.ts.map +1 -0
- package/dist/remote-mcp-edge.js +116 -0
- package/dist/remote-mcp-edge.js.map +1 -0
- package/dist/remote-mcp.d.ts +139 -0
- package/dist/remote-mcp.d.ts.map +1 -0
- package/dist/remote-mcp.js +434 -0
- package/dist/remote-mcp.js.map +1 -0
- package/dist/runtime-secrets.d.ts +12 -0
- package/dist/runtime-secrets.d.ts.map +1 -1
- package/dist/runtime-secrets.js +32 -4
- package/dist/runtime-secrets.js.map +1 -1
- package/dist/seal-key-rotation.d.ts +38 -0
- package/dist/seal-key-rotation.d.ts.map +1 -0
- package/dist/seal-key-rotation.js +206 -0
- package/dist/seal-key-rotation.js.map +1 -0
- package/dist/stack-contract.d.ts +206 -3
- package/dist/stack-contract.d.ts.map +1 -1
- package/dist/stack-contract.js +219 -4
- package/dist/stack-contract.js.map +1 -1
- package/dist/stack-converge.d.ts +53 -1
- package/dist/stack-converge.d.ts.map +1 -1
- package/dist/stack-converge.js +124 -4
- package/dist/stack-converge.js.map +1 -1
- package/dist/state-lock-lease.d.ts +142 -0
- package/dist/state-lock-lease.d.ts.map +1 -0
- package/dist/state-lock-lease.js +347 -0
- package/dist/state-lock-lease.js.map +1 -0
- package/dist/state-store.d.ts +66 -9
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js +249 -30
- package/dist/state-store.js.map +1 -1
- package/dist/upgrade/app-roll.d.ts +36 -7
- package/dist/upgrade/app-roll.d.ts.map +1 -1
- package/dist/upgrade/app-roll.js +53 -19
- package/dist/upgrade/app-roll.js.map +1 -1
- package/dist/upgrade/audit-job-roll.d.ts.map +1 -1
- package/dist/upgrade/audit-job-roll.js +3 -1
- package/dist/upgrade/audit-job-roll.js.map +1 -1
- package/dist/upgrade/converge.d.ts.map +1 -1
- package/dist/upgrade/converge.js +125 -12
- package/dist/upgrade/converge.js.map +1 -1
- package/dist/upgrade/gcp-lookup.d.ts +27 -10
- package/dist/upgrade/gcp-lookup.d.ts.map +1 -1
- package/dist/upgrade/gcp-lookup.js +46 -7
- package/dist/upgrade/gcp-lookup.js.map +1 -1
- package/dist/upgrade/record-rolled.d.ts +40 -0
- package/dist/upgrade/record-rolled.d.ts.map +1 -0
- package/dist/upgrade/record-rolled.js +46 -0
- package/dist/upgrade/record-rolled.js.map +1 -0
- package/dist/upgrade/upgrade.d.ts +45 -1
- package/dist/upgrade/upgrade.d.ts.map +1 -1
- package/dist/upgrade/upgrade.js +60 -17
- package/dist/upgrade/upgrade.js.map +1 -1
- package/dist/upgrader-assess.d.ts +15 -1
- package/dist/upgrader-assess.d.ts.map +1 -1
- package/dist/upgrader-assess.js +36 -6
- package/dist/upgrader-assess.js.map +1 -1
- package/dist/upgrader-gcp.d.ts +15 -0
- package/dist/upgrader-gcp.d.ts.map +1 -1
- package/dist/upgrader-gcp.js +42 -7
- package/dist/upgrader-gcp.js.map +1 -1
- package/dist/upgrader-iam-preflight.d.ts +123 -0
- package/dist/upgrader-iam-preflight.d.ts.map +1 -0
- package/dist/upgrader-iam-preflight.js +414 -0
- package/dist/upgrader-iam-preflight.js.map +1 -0
- package/dist/upgrader-job.d.ts +33 -9
- package/dist/upgrader-job.d.ts.map +1 -1
- package/dist/upgrader-job.js +169 -27
- package/dist/upgrader-job.js.map +1 -1
- package/dist/upgrader-records.d.ts +58 -0
- package/dist/upgrader-records.d.ts.map +1 -0
- package/dist/upgrader-records.js +87 -0
- package/dist/upgrader-records.js.map +1 -0
- package/dist/upgrader-reporter.d.ts +3 -2
- package/dist/upgrader-reporter.d.ts.map +1 -1
- package/dist/upgrader-reporter.js +10 -2
- package/dist/upgrader-reporter.js.map +1 -1
- package/dist/upgrader-setup.d.ts +25 -3
- package/dist/upgrader-setup.d.ts.map +1 -1
- package/dist/upgrader-setup.js +123 -11
- package/dist/upgrader-setup.js.map +1 -1
- package/dist/wizards/gcp-cost.d.ts.map +1 -1
- package/dist/wizards/gcp-cost.js +11 -3
- package/dist/wizards/gcp-cost.js.map +1 -1
- package/dist/wizards/gcp-pulumi.d.ts +6 -0
- package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
- package/dist/wizards/gcp-pulumi.js +14 -2
- package/dist/wizards/gcp-pulumi.js.map +1 -1
- package/dist/wizards/gcp.d.ts.map +1 -1
- package/dist/wizards/gcp.js +106 -4
- package/dist/wizards/gcp.js.map +1 -1
- package/package.json +2 -2
- package/src/agent/run.test.ts +14 -1
- package/src/audit-job-setup.test.ts +7 -1
- package/src/cli-routing.ts +1 -1
- package/src/cli.ts +4 -1
- package/src/compose.ts +5 -0
- package/src/converge-checkpoint.ts +75 -0
- package/src/converge-entrypoint.test.ts +213 -0
- package/src/converge-job-gcp.test.ts +222 -0
- package/src/converge-job-gcp.ts +443 -0
- package/src/converge-job-setup.test.ts +152 -0
- package/src/converge-job-setup.ts +268 -0
- package/src/converge-job.test.ts +486 -0
- package/src/converge-job.ts +542 -0
- package/src/gateway-env-isolation.test.ts +8 -1
- package/src/gcp-names.ts +15 -0
- package/src/gcp-url-map.test.ts +9 -1
- package/src/index.ts +8 -0
- package/src/infra-contract.test.ts +8 -4
- package/src/infra-contract.ts +33 -1
- package/src/install-job.test.ts +3 -1
- package/src/install-job.ts +31 -14
- package/src/lifecycle.ts +137 -71
- package/src/operator-access.test.ts +24 -1
- package/src/operator-access.ts +44 -6
- package/src/release-contract.ts +5 -2
- package/src/release-manifest.test.ts +42 -0
- package/src/release-manifest.ts +2 -0
- package/src/remote-mcp-edge.ts +137 -0
- package/src/remote-mcp.test.ts +690 -0
- package/src/remote-mcp.ts +524 -0
- package/src/runtime-secrets.test.ts +31 -6
- package/src/runtime-secrets.ts +41 -3
- package/src/seal-key-rotation.test.ts +131 -0
- package/src/seal-key-rotation.ts +258 -0
- package/src/stack-contract-parity.test.ts +20 -3
- package/src/stack-contract.test.ts +32 -2
- package/src/stack-contract.ts +363 -4
- package/src/stack-converge-secrets.test.ts +4 -0
- package/src/stack-converge.test.ts +21 -5
- package/src/stack-converge.ts +162 -7
- package/src/state-attach.test.ts +7 -1
- package/src/state-lock-lease.test.ts +302 -0
- package/src/state-lock-lease.ts +423 -0
- package/src/state-lock.test.ts +568 -0
- package/src/state-store.ts +347 -27
- package/src/upgrade/app-roll.ts +102 -21
- package/src/upgrade/audit-job-roll.ts +3 -1
- package/src/upgrade/converge.ts +148 -10
- package/src/upgrade/gcp-lookup.test.ts +80 -0
- package/src/upgrade/gcp-lookup.ts +65 -8
- package/src/upgrade/record-rolled.test.ts +105 -0
- package/src/upgrade/record-rolled.ts +91 -0
- package/src/upgrade/upgrade.test.ts +203 -3
- package/src/upgrade/upgrade.ts +120 -24
- package/src/upgrader-assess.test.ts +92 -3
- package/src/upgrader-assess.ts +58 -9
- package/src/upgrader-gcp.ts +47 -8
- package/src/upgrader-iam-preflight.test.ts +393 -0
- package/src/upgrader-iam-preflight.ts +510 -0
- package/src/upgrader-job.test.ts +586 -15
- package/src/upgrader-job.ts +237 -53
- package/src/upgrader-records.ts +151 -0
- package/src/upgrader-reporter.test.ts +6 -0
- package/src/upgrader-reporter.ts +7 -2
- package/src/upgrader-setup.test.ts +3 -3
- package/src/upgrader-setup.ts +128 -11
- package/src/wizards/gcp-cost.ts +12 -3
- package/src/wizards/gcp-pulumi.test.ts +15 -0
- package/src/wizards/gcp-pulumi.ts +18 -2
- package/src/wizards/gcp.ts +134 -4
- package/stacks/aws/index.ts +10 -0
- package/stacks/azure/index.ts +9 -0
- package/stacks/gcp/index.ts +47 -7
- package/stacks/gcp-firebase/index.ts +38 -6
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { HttpFetch } from "./runtime-secrets.js";
|
|
2
|
+
/** A resource IAM can be asked about. */
|
|
3
|
+
export type IamResource = {
|
|
4
|
+
readonly kind: "project";
|
|
5
|
+
readonly projectId: string;
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: "service-account";
|
|
8
|
+
readonly projectId: string;
|
|
9
|
+
readonly email: string;
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: "run-job";
|
|
12
|
+
readonly projectId: string;
|
|
13
|
+
readonly region: string;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
} | {
|
|
16
|
+
readonly kind: "bucket";
|
|
17
|
+
readonly bucket: string;
|
|
18
|
+
}
|
|
19
|
+
/** Objects under `prefix/` in `bucket`, behind a name-conditioned grant (probed). */
|
|
20
|
+
| {
|
|
21
|
+
readonly kind: "bucket-objects";
|
|
22
|
+
readonly bucket: string;
|
|
23
|
+
readonly prefix: string;
|
|
24
|
+
} | {
|
|
25
|
+
readonly kind: "artifact-repository";
|
|
26
|
+
readonly projectId: string;
|
|
27
|
+
readonly location: string;
|
|
28
|
+
readonly repository: string;
|
|
29
|
+
};
|
|
30
|
+
/** One thing the run will do, the permissions it takes, and where. */
|
|
31
|
+
export interface AccessCheck {
|
|
32
|
+
/** What the upgrader could not do without them, in plain words. */
|
|
33
|
+
readonly need: string;
|
|
34
|
+
readonly resource: IamResource;
|
|
35
|
+
readonly permissions: readonly string[];
|
|
36
|
+
}
|
|
37
|
+
/** A check with the permissions it lacks. */
|
|
38
|
+
export interface MissingAccess {
|
|
39
|
+
readonly check: AccessCheck;
|
|
40
|
+
readonly missing: readonly string[];
|
|
41
|
+
/**
|
|
42
|
+
* Permissions that could not be judged because another one is missing
|
|
43
|
+
* first (storage.objects.get needs storage.objects.list to be testable).
|
|
44
|
+
*/
|
|
45
|
+
readonly unchecked?: readonly string[];
|
|
46
|
+
}
|
|
47
|
+
/** The permissions IAM confirmed on one resource, or why it could not be asked. */
|
|
48
|
+
export type GrantedResult = {
|
|
49
|
+
readonly ok: true;
|
|
50
|
+
readonly granted: readonly string[];
|
|
51
|
+
} | {
|
|
52
|
+
readonly ok: false;
|
|
53
|
+
readonly error: string;
|
|
54
|
+
};
|
|
55
|
+
/** Asks IAM which of `permissions` the job's identity holds on `resource`. */
|
|
56
|
+
export interface IamTester {
|
|
57
|
+
granted(resource: IamResource, permissions: readonly string[]): Promise<GrantedResult>;
|
|
58
|
+
}
|
|
59
|
+
/** How the self-check came out. */
|
|
60
|
+
export type PreflightOutcome = {
|
|
61
|
+
readonly kind: "ok";
|
|
62
|
+
} | {
|
|
63
|
+
readonly kind: "missing";
|
|
64
|
+
readonly missing: readonly MissingAccess[];
|
|
65
|
+
}
|
|
66
|
+
/** IAM could not be asked about at least one resource; nothing was judged missing. */
|
|
67
|
+
| {
|
|
68
|
+
readonly kind: "error";
|
|
69
|
+
readonly errors: readonly string[];
|
|
70
|
+
};
|
|
71
|
+
/** The object the `install/` probes name. It is never written: every probe fails its precondition. */
|
|
72
|
+
export declare const PREFLIGHT_PROBE_OBJECT = ".caelo-iam-preflight";
|
|
73
|
+
/**
|
|
74
|
+
* Every permission an upgrader run needs after its decision, grouped by
|
|
75
|
+
* what it is for (upgrade/app-roll.ts, migration-runner.ts, backup.ts,
|
|
76
|
+
* health.ts, state-store.ts, the self-update in upgrader-job.ts). Keep in
|
|
77
|
+
* step with the grants in upgrader-setup.ts: a permission listed here that
|
|
78
|
+
* setup does not grant stops every update.
|
|
79
|
+
*/
|
|
80
|
+
export declare function upgraderAccessChecks(target: {
|
|
81
|
+
readonly projectId: string;
|
|
82
|
+
readonly region: string;
|
|
83
|
+
readonly env: string;
|
|
84
|
+
}): AccessCheck[];
|
|
85
|
+
/** Ask IAM about every check (one call per resource); never throws. */
|
|
86
|
+
export declare function runIamPreflight(checks: readonly AccessCheck[], tester: IamTester): Promise<PreflightOutcome>;
|
|
87
|
+
/**
|
|
88
|
+
* The request's end reason for missing permissions: every one of them, each
|
|
89
|
+
* with what it is for, then the one next step.
|
|
90
|
+
*/
|
|
91
|
+
export declare function preflightMissingReason(missing: readonly MissingAccess[]): string;
|
|
92
|
+
/** The request's end reason when IAM could not be asked. */
|
|
93
|
+
export declare function preflightErrorReason(errors: readonly string[]): string;
|
|
94
|
+
/** A resource's full name, as IAM and the messages name it. */
|
|
95
|
+
export declare function resourceName(r: IamResource): string;
|
|
96
|
+
/**
|
|
97
|
+
* The real {@link IamTester}: Google's REST APIs with the job's own access
|
|
98
|
+
* token. `token` is asked once per call (gcloud caches it).
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* const tester = restIamTester({ http: fetch, token: async () => "<access token>" });
|
|
102
|
+
* await tester.granted({ kind: "project", projectId: "acme" }, ["run.services.get"]);
|
|
103
|
+
*/
|
|
104
|
+
export declare function restIamTester(deps: {
|
|
105
|
+
readonly http: HttpFetch;
|
|
106
|
+
readonly token: () => Promise<string>;
|
|
107
|
+
}): IamTester;
|
|
108
|
+
/** The testIamPermissions request for a resource that has one. */
|
|
109
|
+
export declare function testIamCall(resource: Exclude<IamResource, {
|
|
110
|
+
kind: "bucket-objects";
|
|
111
|
+
}>, permissions: readonly string[]): {
|
|
112
|
+
url: string;
|
|
113
|
+
method: "GET" | "POST";
|
|
114
|
+
body?: string;
|
|
115
|
+
};
|
|
116
|
+
/** The precondition-guarded request that tests one object permission (see the file comment). */
|
|
117
|
+
export declare function objectProbeCall(resource: Extract<IamResource, {
|
|
118
|
+
kind: "bucket-objects";
|
|
119
|
+
}>, permission: string): {
|
|
120
|
+
url: string;
|
|
121
|
+
method: "GET" | "POST" | "DELETE";
|
|
122
|
+
} | null;
|
|
123
|
+
//# sourceMappingURL=upgrader-iam-preflight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrader-iam-preflight.d.ts","sourceRoot":"","sources":["../src/upgrader-iam-preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
/**
|
|
3
|
+
* The upgrader job's IAM self-check (#818): before it takes the state lock
|
|
4
|
+
* or changes anything, the job asks Google Cloud whether its own service
|
|
5
|
+
* account holds every permission the run will need, and stops with ALL the
|
|
6
|
+
* missing ones named when it does not.
|
|
7
|
+
*
|
|
8
|
+
* Why: the first live in-app update died at its first bucket write (#799)
|
|
9
|
+
* because a grant that looked right could never match the call. Unit tests
|
|
10
|
+
* against a fake gcloud cannot catch that class of bug; asking IAM can, and
|
|
11
|
+
* asking first means a missing grant ends the run before the backup, the
|
|
12
|
+
* migrations or the roll, with a reason a non-technical Owner can pass on
|
|
13
|
+
* ("Run the upgrade once with Cloud Shell" re-converges the grants).
|
|
14
|
+
*
|
|
15
|
+
* How each permission is checked, without doing the action:
|
|
16
|
+
*
|
|
17
|
+
* - `testIamPermissions` on the resource the call acts on — the project,
|
|
18
|
+
* a service account, the upgrader's own job, the ops bucket, the release
|
|
19
|
+
* image repository. Google returns the subset the CALLER holds; no
|
|
20
|
+
* permission is needed to ask.
|
|
21
|
+
* - The `install/` objects in the ops bucket are the exception. The
|
|
22
|
+
* upgrader's object rights there are an IAM grant CONDITIONED on the
|
|
23
|
+
* object name (upgrader-setup.ts `installStateCondition`), and a bucket's
|
|
24
|
+
* testIamPermissions evaluates conditions against the bucket's own
|
|
25
|
+
* resource name, never an object's — it would report a grant that works
|
|
26
|
+
* as missing (the mirror image of #799). Cloud Storage has no object
|
|
27
|
+
* testIamPermissions on uniform-access buckets, so those three are
|
|
28
|
+
* probed with requests that can never change anything: a read of an
|
|
29
|
+
* object that does not exist, and an upload + a delete preconditioned
|
|
30
|
+
* on generation 1, which no object ever has. Cloud Storage authorizes a
|
|
31
|
+
* request before it evaluates its preconditions, so 403 means the
|
|
32
|
+
* permission is missing and 404/412 means it is held — with one
|
|
33
|
+
* exception: a read of an absent object is answered 403 whenever the
|
|
34
|
+
* caller lacks storage.objects.list, so objects.get is probed only once
|
|
35
|
+
* the bucket check confirmed list, and reported "unchecked" otherwise.
|
|
36
|
+
*
|
|
37
|
+
* Direct VPC egress (#818): the migration job and both services are
|
|
38
|
+
* deployed with `--network/--subnet`. Google documents the compute
|
|
39
|
+
* permissions for that (`compute.subnetworks.use`, …) on the Cloud Run
|
|
40
|
+
* SERVICE AGENT (roles/run.serviceAgent has them), not on the principal
|
|
41
|
+
* that deploys, so they are not checked here and the upgrader gets no
|
|
42
|
+
* compute role (UPGRADER_FORBIDDEN_ROLES). The real-GCP upgrader smoke test
|
|
43
|
+
* (.github/workflows/upgrader-smoke.yml) is where that shows on real GCP:
|
|
44
|
+
* each stable release, installed on the smoke install through Settings →
|
|
45
|
+
* Updates, must serve cleanly before the next tag ships.
|
|
46
|
+
*
|
|
47
|
+
* Everything here is pure except {@link restIamTester}, which takes its
|
|
48
|
+
* HTTP client and token source as arguments.
|
|
49
|
+
*/
|
|
50
|
+
import { z } from "zod";
|
|
51
|
+
import { auditServiceAccountEmail, gatewayServiceAccountEmail, mcpIapServiceAccountEmail, runServiceAccountEmail, upgraderJobName, upgraderServiceAccountEmail, } from "./gcp-names.js";
|
|
52
|
+
import { OPS_INSTALL_PREFIX, opsBucketName } from "./state-backend.js";
|
|
53
|
+
import { RELEASE_REGISTRY } from "./upgrade/release-images.js";
|
|
54
|
+
/** The object the `install/` probes name. It is never written: every probe fails its precondition. */
|
|
55
|
+
export const PREFLIGHT_PROBE_OBJECT = ".caelo-iam-preflight";
|
|
56
|
+
/**
|
|
57
|
+
* Every permission an upgrader run needs after its decision, grouped by
|
|
58
|
+
* what it is for (upgrade/app-roll.ts, migration-runner.ts, backup.ts,
|
|
59
|
+
* health.ts, state-store.ts, the self-update in upgrader-job.ts). Keep in
|
|
60
|
+
* step with the grants in upgrader-setup.ts: a permission listed here that
|
|
61
|
+
* setup does not grant stops every update.
|
|
62
|
+
*/
|
|
63
|
+
export function upgraderAccessChecks(target) {
|
|
64
|
+
const { projectId, region, env } = target;
|
|
65
|
+
const project = { kind: "project", projectId };
|
|
66
|
+
const sa = (email) => ({ kind: "service-account", projectId, email });
|
|
67
|
+
const bucket = opsBucketName(projectId);
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
need: "read which release the site's admin and gateway serve",
|
|
71
|
+
resource: project,
|
|
72
|
+
permissions: [
|
|
73
|
+
"run.services.get",
|
|
74
|
+
"run.services.list",
|
|
75
|
+
"run.revisions.get",
|
|
76
|
+
"run.revisions.list",
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
need: "deploy the new release to the admin and gateway and move their traffic",
|
|
81
|
+
resource: project,
|
|
82
|
+
permissions: ["run.services.update", "run.operations.get"],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
need: "run the database migrations as a one-off job (and check whether an earlier run still holds the install's lock)",
|
|
86
|
+
resource: project,
|
|
87
|
+
permissions: [
|
|
88
|
+
"run.jobs.create",
|
|
89
|
+
"run.jobs.delete",
|
|
90
|
+
"run.jobs.get",
|
|
91
|
+
"run.jobs.run",
|
|
92
|
+
"run.executions.get",
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
need: "back up the database before anything changes",
|
|
97
|
+
resource: project,
|
|
98
|
+
permissions: [
|
|
99
|
+
"cloudsql.backupRuns.create",
|
|
100
|
+
"cloudsql.backupRuns.get",
|
|
101
|
+
"cloudsql.backupRuns.list",
|
|
102
|
+
"cloudsql.instances.get",
|
|
103
|
+
"cloudsql.instances.list",
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
need: "run the admin and the migration job as the admin's service account",
|
|
108
|
+
resource: sa(runServiceAccountEmail(projectId, env)),
|
|
109
|
+
permissions: ["iam.serviceAccounts.actAs"],
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
need: "run the gateway as its service account",
|
|
113
|
+
resource: sa(gatewayServiceAccountEmail(projectId, env)),
|
|
114
|
+
permissions: ["iam.serviceAccounts.actAs"],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
// #875 — every app-roll moves the audit job onto the new admin image
|
|
118
|
+
// (upgrade/audit-job-roll.ts), and upgrader-setup.ts grants actAs on
|
|
119
|
+
// the job's account for exactly that.
|
|
120
|
+
need: "move the quality-audit job onto the new admin image",
|
|
121
|
+
resource: project,
|
|
122
|
+
permissions: ["run.jobs.update"],
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
need: "run the quality-audit job as its service account after moving it",
|
|
126
|
+
resource: sa(auditServiceAccountEmail(projectId, env)),
|
|
127
|
+
permissions: ["iam.serviceAccounts.actAs"],
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
need: "keep the upgrader job running as its own service account after it updates itself",
|
|
131
|
+
resource: sa(upgraderServiceAccountEmail(projectId, env)),
|
|
132
|
+
permissions: ["iam.serviceAccounts.actAs"],
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
need: "sign in to the admin (through its identity proxy) for the health check",
|
|
136
|
+
resource: sa(mcpIapServiceAccountEmail(projectId)),
|
|
137
|
+
permissions: ["iam.serviceAccounts.signJwt"],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
need: "move the upgrader itself onto the new release",
|
|
141
|
+
resource: { kind: "run-job", projectId, region, name: upgraderJobName(env) },
|
|
142
|
+
permissions: ["run.jobs.get", "run.jobs.update"],
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
need: "list the install's ops bucket (every write there lists it first)",
|
|
146
|
+
resource: { kind: "bucket", bucket },
|
|
147
|
+
permissions: ["storage.objects.list"],
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
need: `read and write the install's records and its state lock (${OPS_INSTALL_PREFIX}/ in the ops bucket)`,
|
|
151
|
+
resource: { kind: "bucket-objects", bucket, prefix: OPS_INSTALL_PREFIX },
|
|
152
|
+
permissions: ["storage.objects.get", "storage.objects.create", "storage.objects.delete"],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
need: "read Caelo's release images",
|
|
156
|
+
resource: {
|
|
157
|
+
kind: "artifact-repository",
|
|
158
|
+
projectId: RELEASE_REGISTRY.project,
|
|
159
|
+
location: RELEASE_REGISTRY.region,
|
|
160
|
+
repository: RELEASE_REGISTRY.repo,
|
|
161
|
+
},
|
|
162
|
+
permissions: ["artifactregistry.repositories.downloadArtifacts"],
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
}
|
|
166
|
+
/** Ask IAM about every check (one call per resource); never throws. */
|
|
167
|
+
export async function runIamPreflight(checks, tester) {
|
|
168
|
+
// One question per resource: the project alone carries four checks.
|
|
169
|
+
const byResource = new Map();
|
|
170
|
+
for (const c of checks) {
|
|
171
|
+
const key = resourceName(c.resource);
|
|
172
|
+
const entry = byResource.get(key) ?? { resource: c.resource, permissions: new Set() };
|
|
173
|
+
for (const p of c.permissions)
|
|
174
|
+
entry.permissions.add(p);
|
|
175
|
+
byResource.set(key, entry);
|
|
176
|
+
}
|
|
177
|
+
const granted = new Map();
|
|
178
|
+
/** Permissions that could not be judged (see the object probes below). */
|
|
179
|
+
const unverified = new Map();
|
|
180
|
+
const errors = [];
|
|
181
|
+
const ask = async (key, resource, permissions) => {
|
|
182
|
+
let r;
|
|
183
|
+
try {
|
|
184
|
+
r = await tester.granted(resource, [...permissions].sort());
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
r = { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
188
|
+
}
|
|
189
|
+
if (r.ok)
|
|
190
|
+
granted.set(key, new Set(r.granted));
|
|
191
|
+
else
|
|
192
|
+
errors.push(`${key}: ${r.error}`);
|
|
193
|
+
};
|
|
194
|
+
const entries = [...byResource];
|
|
195
|
+
await Promise.all(entries
|
|
196
|
+
.filter(([, e]) => e.resource.kind !== "bucket-objects")
|
|
197
|
+
.map(([key, e]) => ask(key, e.resource, [...e.permissions])));
|
|
198
|
+
// The object probes go second: Cloud Storage answers a read of an absent
|
|
199
|
+
// object with 403 — not 404 — when the caller lacks storage.objects.list
|
|
200
|
+
// on the bucket, so the objects.get probe means something only once list
|
|
201
|
+
// is confirmed. Without it, get is reported as unchecked, never missing.
|
|
202
|
+
await Promise.all(entries
|
|
203
|
+
.filter(([, e]) => e.resource.kind === "bucket-objects")
|
|
204
|
+
.map(([key, e]) => {
|
|
205
|
+
const r = e.resource;
|
|
206
|
+
const listHeld = granted.get(resourceName({ kind: "bucket", bucket: r.bucket }))?.has(OBJECT_LIST) ??
|
|
207
|
+
false;
|
|
208
|
+
const asked = [...e.permissions].filter((p) => listHeld || p !== OBJECT_GET);
|
|
209
|
+
if (asked.length < e.permissions.size)
|
|
210
|
+
unverified.set(key, new Set([OBJECT_GET]));
|
|
211
|
+
return ask(key, e.resource, asked);
|
|
212
|
+
}));
|
|
213
|
+
if (errors.length > 0)
|
|
214
|
+
return { kind: "error", errors: errors.sort() };
|
|
215
|
+
const missing = [];
|
|
216
|
+
for (const check of checks) {
|
|
217
|
+
const key = resourceName(check.resource);
|
|
218
|
+
const held = granted.get(key) ?? new Set();
|
|
219
|
+
const unknown = unverified.get(key) ?? new Set();
|
|
220
|
+
const lacking = check.permissions.filter((p) => !held.has(p) && !unknown.has(p));
|
|
221
|
+
const unchecked = check.permissions.filter((p) => unknown.has(p));
|
|
222
|
+
if (lacking.length > 0 || unchecked.length > 0) {
|
|
223
|
+
missing.push({ check, missing: lacking, ...(unchecked.length > 0 ? { unchecked } : {}) });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return missing.length === 0 ? { kind: "ok" } : { kind: "missing", missing };
|
|
227
|
+
}
|
|
228
|
+
const OBJECT_LIST = "storage.objects.list";
|
|
229
|
+
const OBJECT_GET = "storage.objects.get";
|
|
230
|
+
/**
|
|
231
|
+
* The request's end reason for missing permissions: every one of them, each
|
|
232
|
+
* with what it is for, then the one next step.
|
|
233
|
+
*/
|
|
234
|
+
export function preflightMissingReason(missing) {
|
|
235
|
+
const lines = missing.map((m) => {
|
|
236
|
+
const where = resourceName(m.check.resource);
|
|
237
|
+
const parts = [
|
|
238
|
+
...(m.missing.length > 0 ? [`missing ${m.missing.join(", ")} on ${where}`] : []),
|
|
239
|
+
...(m.unchecked && m.unchecked.length > 0
|
|
240
|
+
? [
|
|
241
|
+
`${m.unchecked.join(", ")} on ${where} could not be checked: without storage.objects.list Cloud Storage answers 403 for any absent object, held or not`,
|
|
242
|
+
]
|
|
243
|
+
: []),
|
|
244
|
+
];
|
|
245
|
+
return `it ${m.missing.length > 0 ? "cannot" : "may not be able to"} ${m.check.need} (${parts.join("; ")})`;
|
|
246
|
+
});
|
|
247
|
+
return `Nothing was changed: the upgrader checked its access before starting and is missing permissions this update needs — ${lines.join("; ")}. Run the upgrade once with Cloud Shell; it repairs the upgrader's access.`;
|
|
248
|
+
}
|
|
249
|
+
/** The request's end reason when IAM could not be asked. */
|
|
250
|
+
export function preflightErrorReason(errors) {
|
|
251
|
+
return `Nothing was changed: the upgrader could not check its access before starting (${errors.join("; ")}). Try again in a few minutes; if it keeps failing, run the upgrade once with Cloud Shell.`;
|
|
252
|
+
}
|
|
253
|
+
/** A resource's full name, as IAM and the messages name it. */
|
|
254
|
+
export function resourceName(r) {
|
|
255
|
+
switch (r.kind) {
|
|
256
|
+
case "project":
|
|
257
|
+
return `projects/${r.projectId}`;
|
|
258
|
+
case "service-account":
|
|
259
|
+
return `projects/${r.projectId}/serviceAccounts/${r.email}`;
|
|
260
|
+
case "run-job":
|
|
261
|
+
return `projects/${r.projectId}/locations/${r.region}/jobs/${r.name}`;
|
|
262
|
+
case "bucket":
|
|
263
|
+
return `gs://${r.bucket}`;
|
|
264
|
+
case "bucket-objects":
|
|
265
|
+
return `gs://${r.bucket}/${r.prefix}/`;
|
|
266
|
+
case "artifact-repository":
|
|
267
|
+
return `projects/${r.projectId}/locations/${r.location}/repositories/${r.repository}`;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const testIamResponse = z.object({ permissions: z.array(z.string()).optional() });
|
|
271
|
+
/**
|
|
272
|
+
* The real {@link IamTester}: Google's REST APIs with the job's own access
|
|
273
|
+
* token. `token` is asked once per call (gcloud caches it).
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* const tester = restIamTester({ http: fetch, token: async () => "<access token>" });
|
|
277
|
+
* await tester.granted({ kind: "project", projectId: "acme" }, ["run.services.get"]);
|
|
278
|
+
*/
|
|
279
|
+
export function restIamTester(deps) {
|
|
280
|
+
return {
|
|
281
|
+
async granted(resource, permissions) {
|
|
282
|
+
const bearer = await deps.token();
|
|
283
|
+
if (!bearer)
|
|
284
|
+
return { ok: false, error: "no access token for the upgrader's account" };
|
|
285
|
+
const auth = { Authorization: `Bearer ${bearer}` };
|
|
286
|
+
if (resource.kind === "bucket-objects") {
|
|
287
|
+
return probeObjects(deps.http, auth, resource, permissions);
|
|
288
|
+
}
|
|
289
|
+
const call = testIamCall(resource, permissions);
|
|
290
|
+
const res = await deps.http(call.url, {
|
|
291
|
+
method: call.method,
|
|
292
|
+
headers: { ...auth, ...(call.body ? { "Content-Type": "application/json" } : {}) },
|
|
293
|
+
...(call.body ? { body: call.body } : {}),
|
|
294
|
+
});
|
|
295
|
+
const text = await res.text();
|
|
296
|
+
if (!res.ok)
|
|
297
|
+
return { ok: false, error: `HTTP ${res.status} ${text.trim().slice(0, 300)}` };
|
|
298
|
+
let raw;
|
|
299
|
+
try {
|
|
300
|
+
raw = JSON.parse(text);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return { ok: false, error: "testIamPermissions answered without JSON" };
|
|
304
|
+
}
|
|
305
|
+
const parsed = testIamResponse.safeParse(raw);
|
|
306
|
+
if (!parsed.success) {
|
|
307
|
+
return { ok: false, error: "testIamPermissions answered without a permission list" };
|
|
308
|
+
}
|
|
309
|
+
// An empty answer omits `permissions`: none of them is held.
|
|
310
|
+
return { ok: true, granted: parsed.data.permissions ?? [] };
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
/** The testIamPermissions request for a resource that has one. */
|
|
315
|
+
export function testIamCall(resource, permissions) {
|
|
316
|
+
const body = JSON.stringify({ permissions });
|
|
317
|
+
switch (resource.kind) {
|
|
318
|
+
case "project":
|
|
319
|
+
return {
|
|
320
|
+
url: `https://cloudresourcemanager.googleapis.com/v1/projects/${encodeURIComponent(resource.projectId)}:testIamPermissions`,
|
|
321
|
+
method: "POST",
|
|
322
|
+
body,
|
|
323
|
+
};
|
|
324
|
+
case "service-account":
|
|
325
|
+
return {
|
|
326
|
+
url: `https://iam.googleapis.com/v1/projects/${encodeURIComponent(resource.projectId)}/serviceAccounts/${encodeURIComponent(resource.email)}:testIamPermissions`,
|
|
327
|
+
method: "POST",
|
|
328
|
+
body,
|
|
329
|
+
};
|
|
330
|
+
case "run-job":
|
|
331
|
+
return {
|
|
332
|
+
url: `https://run.googleapis.com/v2/projects/${encodeURIComponent(resource.projectId)}/locations/${encodeURIComponent(resource.region)}/jobs/${encodeURIComponent(resource.name)}:testIamPermissions`,
|
|
333
|
+
method: "POST",
|
|
334
|
+
body,
|
|
335
|
+
};
|
|
336
|
+
case "bucket": {
|
|
337
|
+
const q = permissions.map((p) => `permissions=${encodeURIComponent(p)}`).join("&");
|
|
338
|
+
return {
|
|
339
|
+
url: `https://storage.googleapis.com/storage/v1/b/${encodeURIComponent(resource.bucket)}/iam/testPermissions?${q}`,
|
|
340
|
+
method: "GET",
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
case "artifact-repository":
|
|
344
|
+
return {
|
|
345
|
+
url: `https://artifactregistry.googleapis.com/v1/projects/${encodeURIComponent(resource.projectId)}/locations/${encodeURIComponent(resource.location)}/repositories/${encodeURIComponent(resource.repository)}:testIamPermissions`,
|
|
346
|
+
method: "POST",
|
|
347
|
+
body,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const OBJECT_PROBES = {
|
|
352
|
+
// A read of an object that does not exist: 404 once authorized.
|
|
353
|
+
"storage.objects.get": { method: "GET", held: [404, 200] },
|
|
354
|
+
// An upload that must match generation 1: never true, so 412 once authorized.
|
|
355
|
+
"storage.objects.create": { method: "POST", held: [412] },
|
|
356
|
+
// A delete that must match generation 1: 404 (absent) or 412 once authorized.
|
|
357
|
+
"storage.objects.delete": { method: "DELETE", held: [404, 412] },
|
|
358
|
+
};
|
|
359
|
+
/** The precondition-guarded request that tests one object permission (see the file comment). */
|
|
360
|
+
export function objectProbeCall(resource, permission) {
|
|
361
|
+
const probe = OBJECT_PROBES[permission];
|
|
362
|
+
if (!probe)
|
|
363
|
+
return null;
|
|
364
|
+
const bucket = encodeURIComponent(resource.bucket);
|
|
365
|
+
const object = encodeURIComponent(`${resource.prefix}/${PREFLIGHT_PROBE_OBJECT}`);
|
|
366
|
+
switch (probe.method) {
|
|
367
|
+
case "GET":
|
|
368
|
+
return {
|
|
369
|
+
url: `https://storage.googleapis.com/storage/v1/b/${bucket}/o/${object}`,
|
|
370
|
+
method: "GET",
|
|
371
|
+
};
|
|
372
|
+
case "POST":
|
|
373
|
+
return {
|
|
374
|
+
url: `https://storage.googleapis.com/upload/storage/v1/b/${bucket}/o?uploadType=media&name=${object}&ifGenerationMatch=1`,
|
|
375
|
+
method: "POST",
|
|
376
|
+
};
|
|
377
|
+
case "DELETE":
|
|
378
|
+
return {
|
|
379
|
+
url: `https://storage.googleapis.com/storage/v1/b/${bucket}/o/${object}?ifGenerationMatch=1`,
|
|
380
|
+
method: "DELETE",
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
async function probeObjects(http, auth, resource, permissions) {
|
|
385
|
+
const granted = [];
|
|
386
|
+
for (const permission of permissions) {
|
|
387
|
+
const call = objectProbeCall(resource, permission);
|
|
388
|
+
const probe = OBJECT_PROBES[permission];
|
|
389
|
+
if (!call || !probe) {
|
|
390
|
+
return { ok: false, error: `no probe for ${permission} on ${resourceName(resource)}` };
|
|
391
|
+
}
|
|
392
|
+
const res = await http(call.url, {
|
|
393
|
+
method: call.method,
|
|
394
|
+
headers: call.method === "POST" ? { ...auth, "Content-Type": "application/octet-stream" } : auth,
|
|
395
|
+
...(call.method === "POST" ? { body: "" } : {}),
|
|
396
|
+
});
|
|
397
|
+
const text = await res.text();
|
|
398
|
+
if (probe.held.includes(res.status)) {
|
|
399
|
+
// 200 on the upload would mean an object was written; held[] for
|
|
400
|
+
// create has no 200, so that lands below as an error, loudly.
|
|
401
|
+
granted.push(permission);
|
|
402
|
+
}
|
|
403
|
+
else if (res.status === 403) {
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
return {
|
|
407
|
+
ok: false,
|
|
408
|
+
error: `probing ${permission} on ${resourceName(resource)}: HTTP ${res.status} ${text.trim().slice(0, 300)}`,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return { ok: true, granted };
|
|
413
|
+
}
|
|
414
|
+
//# sourceMappingURL=upgrader-iam-preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrader-iam-preflight.js","sourceRoot":"","sources":["../src/upgrader-iam-preflight.ts"],"names":[],"mappings":"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"}
|
package/dist/upgrader-job.d.ts
CHANGED
|
@@ -10,18 +10,34 @@
|
|
|
10
10
|
* but cannot change IAM, service accounts or secrets. One run:
|
|
11
11
|
*
|
|
12
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;
|
|
13
|
+
* 2. read what the install runs from install.json in its ops bucket; when
|
|
14
|
+
* it names other images than the ones serving, judge against the
|
|
15
|
+
* serving release if a signed release manifest names both its images,
|
|
16
|
+
* else stop (upgrader-records.ts, #860);
|
|
14
17
|
* 3. fetch the release manifest from the FIXED registry and verify its
|
|
15
18
|
* signature (release-manifest.ts) — no registry, identity or digest
|
|
16
19
|
* ever comes from the request, the env or argv;
|
|
17
20
|
* 4. decide (upgrader-assess.ts): refuse a bad signature, a downgrade, a
|
|
18
21
|
* release below its minFromVersion, an automatic run the policy does
|
|
19
|
-
* not allow; hand an infrastructure change
|
|
22
|
+
* not allow; hand an infrastructure change back (the admin routes
|
|
23
|
+
* it to the converge job, #819; Cloud Shell on installs without one);
|
|
24
|
+
* -- ask IAM whether the job's account holds every permission the rest
|
|
25
|
+
* of the run needs (upgrader-iam-preflight.ts, #818); any missing one
|
|
26
|
+
* ends the run as needs-Cloud-Shell, all of them named, nothing changed;
|
|
27
|
+
* -- only now take the install's state lock (#815), confirm install.json
|
|
28
|
+
* is unchanged since step 2, and write step 2's correction. Everything
|
|
29
|
+
* above only reads, so no lock problem can hide the verdict; a stop
|
|
30
|
+
* verdict still corrects the records when the lock is free. Only a
|
|
31
|
+
* verdict judged on drift asks for the lock even when it stops: the
|
|
32
|
+
* serving images were read without it, and a run that holds it may be
|
|
33
|
+
* half-way through its own roll — then the answer is "another install
|
|
34
|
+
* operation is running", never a drift or Cloud Shell verdict;
|
|
20
35
|
* 5. take a Cloud SQL backup — no backup, no change;
|
|
21
36
|
* 6. app-roll (upgrade/app-roll.ts) with no converge hooks: migrations
|
|
22
37
|
* with the new admin image, admin then gateway, deep health check,
|
|
23
38
|
* traffic back onto the previous revisions on any failure;
|
|
24
|
-
* 7. record the new digests + version in install.json
|
|
39
|
+
* 7. record the new digests + version in install.json (a failed write
|
|
40
|
+
* ends the run as failed, never as succeeded);
|
|
25
41
|
* 8. move this job onto the release's own ops image, last.
|
|
26
42
|
*
|
|
27
43
|
* Every step is narrated into `platform_update_events` (upgrader-reporter.ts).
|
|
@@ -35,6 +51,7 @@ import type { InstallMetadata } from "./install-state.js";
|
|
|
35
51
|
import type { ReleaseManifestResult } from "./release-manifest.js";
|
|
36
52
|
import { type AppRollPlan } from "./upgrade/app-roll.js";
|
|
37
53
|
import type { HealthProbe } from "./upgrade/health.js";
|
|
54
|
+
import { type IamTester } from "./upgrader-iam-preflight.js";
|
|
38
55
|
import { type JobLog } from "./upgrader-reporter.js";
|
|
39
56
|
/** A request the job claimed, with the policy as it stood at claim time. */
|
|
40
57
|
export interface ClaimedUpdate {
|
|
@@ -88,15 +105,22 @@ export interface UpgraderJobDeps {
|
|
|
88
105
|
readonly env: string;
|
|
89
106
|
/** This Cloud Run execution, for the log link; null outside Cloud Run. */
|
|
90
107
|
readonly execution: string | null;
|
|
108
|
+
/**
|
|
109
|
+
* Asks IAM which permissions the job's own account holds (#818), for the
|
|
110
|
+
* self-check right after the decision. Never the action itself.
|
|
111
|
+
*/
|
|
112
|
+
readonly iam: IamTester;
|
|
91
113
|
/** fetchVerifiedReleaseManifest against the fixed registry. */
|
|
92
114
|
readonly fetchManifest: (version: string) => Promise<ReleaseManifestResult>;
|
|
93
115
|
/**
|
|
94
|
-
* Take the install's state lock (the one `pulumi up
|
|
95
|
-
* hold)
|
|
96
|
-
*
|
|
116
|
+
* Take the install's state lock (the one `pulumi up`, `state migrate` and
|
|
117
|
+
* the CLI `upgrade` hold) right before the run's first write; returns its
|
|
118
|
+
* release. Breaks a lock whose holder has ended and reports that through
|
|
119
|
+
* `onBreak` (state-store.ts). Throws StateLockHeldError when a live run
|
|
120
|
+
* holds it, any other error when it cannot be taken or judged.
|
|
97
121
|
*/
|
|
98
|
-
readonly lockState: () => () => void;
|
|
99
|
-
/** install.json
|
|
122
|
+
readonly lockState: (onBreak: (message: string) => void) => () => void;
|
|
123
|
+
/** install.json as the ops bucket holds it NOW (null when absent) — never cached. */
|
|
100
124
|
readonly readInstall: () => InstallMetadata | null;
|
|
101
125
|
readonly writeInstall: (meta: InstallMetadata) => void;
|
|
102
126
|
/** Runs both migration targets with the NEW admin image. */
|
|
@@ -129,5 +153,5 @@ export declare function runUpgraderJob(deps: UpgraderJobDeps): Promise<UpgraderR
|
|
|
129
153
|
*
|
|
130
154
|
* @returns whether the end was written (false after the last attempt failed)
|
|
131
155
|
*/
|
|
132
|
-
export declare function recordUpdateEnd(queue: UpdateQueue, requestId: string, end: UpdateEnd, log: JobLog, sleep?: Sleep): Promise<boolean>;
|
|
156
|
+
export declare function recordUpdateEnd(queue: Pick<UpdateQueue, "finish">, requestId: string, end: UpdateEnd, log: JobLog, sleep?: Sleep): Promise<boolean>;
|
|
133
157
|
//# sourceMappingURL=upgrader-job.d.ts.map
|