@caelo-cms/provisioning 0.10.38 → 0.10.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin-domain.d.ts +21 -3
- package/dist/admin-domain.d.ts.map +1 -1
- package/dist/admin-domain.js +88 -16
- package/dist/admin-domain.js.map +1 -1
- package/dist/cert-wait.d.ts +84 -0
- package/dist/cert-wait.d.ts.map +1 -0
- package/dist/cert-wait.js +95 -0
- package/dist/cert-wait.js.map +1 -0
- package/dist/checkpoint-secrets.d.ts +88 -0
- package/dist/checkpoint-secrets.d.ts.map +1 -0
- package/dist/checkpoint-secrets.js +245 -0
- package/dist/checkpoint-secrets.js.map +1 -0
- package/dist/cli.js +160 -28
- package/dist/cli.js.map +1 -1
- package/dist/dns/groups.d.ts +36 -0
- package/dist/dns/groups.d.ts.map +1 -0
- package/dist/dns/groups.js +46 -0
- package/dist/dns/groups.js.map +1 -0
- package/dist/gcloud-components.d.ts +42 -0
- package/dist/gcloud-components.d.ts.map +1 -0
- package/dist/gcloud-components.js +53 -0
- package/dist/gcloud-components.js.map +1 -0
- package/dist/gcloud.d.ts +14 -0
- package/dist/gcloud.d.ts.map +1 -1
- package/dist/gcloud.js +18 -5
- package/dist/gcloud.js.map +1 -1
- package/dist/install-state.d.ts +19 -1
- package/dist/install-state.d.ts.map +1 -1
- package/dist/install-state.js +37 -3
- package/dist/install-state.js.map +1 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +24 -3
- package/dist/lifecycle.js.map +1 -1
- package/dist/redirects-emit.d.ts +0 -13
- package/dist/redirects-emit.d.ts.map +1 -1
- package/dist/redirects-emit.js +19 -0
- package/dist/redirects-emit.js.map +1 -1
- package/dist/regions.d.ts.map +1 -1
- package/dist/regions.js +4 -1
- package/dist/regions.js.map +1 -1
- package/dist/runtime-secrets.d.ts +13 -1
- package/dist/runtime-secrets.d.ts.map +1 -1
- package/dist/runtime-secrets.js +40 -4
- package/dist/runtime-secrets.js.map +1 -1
- package/dist/self-hosted-roles.d.ts.map +1 -1
- package/dist/self-hosted-roles.js +2 -0
- package/dist/self-hosted-roles.js.map +1 -1
- package/dist/stack-contract.d.ts +16 -2
- package/dist/stack-contract.d.ts.map +1 -1
- package/dist/stack-contract.js +24 -1
- package/dist/stack-contract.js.map +1 -1
- package/dist/wizard.d.ts +5 -19
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +53 -4
- package/dist/wizard.js.map +1 -1
- package/dist/wizards/gcp-pulumi.d.ts +21 -0
- package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
- package/dist/wizards/gcp-pulumi.js +25 -5
- package/dist/wizards/gcp-pulumi.js.map +1 -1
- package/dist/wizards/gcp.d.ts.map +1 -1
- package/dist/wizards/gcp.js +160 -116
- package/dist/wizards/gcp.js.map +1 -1
- package/package.json +3 -3
- package/stacks/gcp/index.ts +20 -3
- package/stacks/gcp-firebase/index.ts +20 -5
- package/stacks/self-hosted/index.ts +3 -1
package/dist/admin-domain.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `cms-provision admin-domain enable` — serve a gcp-firebase admin at
|
|
3
|
-
* `admin.<domain>` instead of its `*.run.app` URL
|
|
3
|
+
* `admin.<domain>` instead of its `*.run.app` URL — and, the same way,
|
|
4
|
+
* `cms-provision staging-domain enable` (#650): serve the protected staging
|
|
5
|
+
* site at `staging.<domain>` from the gateway, which gates and proxies it.
|
|
4
6
|
*
|
|
5
7
|
* The gcp-firebase stack only creates that Cloud Run domain mapping behind
|
|
6
8
|
* `provisionAdminDomain=true`, off by default: Google refuses a mapping
|
|
@@ -44,11 +46,27 @@ export type AdminDomainResult = {
|
|
|
44
46
|
status: "failed";
|
|
45
47
|
error: string;
|
|
46
48
|
};
|
|
47
|
-
|
|
49
|
+
/** What `staging-domain enable` reports; same shape as the admin's. */
|
|
50
|
+
export type StagingDomainResult = AdminDomainResult;
|
|
51
|
+
interface DomainOpts {
|
|
48
52
|
projectId: string;
|
|
49
53
|
region: string;
|
|
50
54
|
domain: string;
|
|
51
55
|
run?: Run;
|
|
52
|
-
|
|
56
|
+
/** Progress lines (the one-time `beta` component install); defaults to stdout. */
|
|
57
|
+
log?: (message: string) => void;
|
|
58
|
+
}
|
|
59
|
+
export declare function enableAdminDomain(opts: DomainOpts): Promise<AdminDomainResult>;
|
|
60
|
+
/**
|
|
61
|
+
* #650 — map the install's staging host (its setting, default
|
|
62
|
+
* `staging.<domain>`; any FQDN) to the gateway, which serves the protected
|
|
63
|
+
* staging site. Cloud Run refuses domain-mapped requests while a service's
|
|
64
|
+
* ingress is internal-only, so the gateway's ingress is opened first (the
|
|
65
|
+
* stack declares the same since #650; installs from before keep the old
|
|
66
|
+
* value until this runs). Idempotent like the admin mapping.
|
|
67
|
+
*/
|
|
68
|
+
export declare function enableStagingDomain(opts: DomainOpts & {
|
|
69
|
+
stagingHost: string;
|
|
70
|
+
}): Promise<StagingDomainResult>;
|
|
53
71
|
export {};
|
|
54
72
|
//# sourceMappingURL=admin-domain.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-domain.d.ts","sourceRoot":"","sources":["../src/admin-domain.ts"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"admin-domain.d.ts","sourceRoot":"","sources":["../src/admin-domain.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAA2B,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAGzE,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,cAAc,EAAE,CAAA;CAAE,GACnF;IAAE,MAAM,EAAE,oBAAoB,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACxE;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,uEAAuE;AACvE,MAAM,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAepD,UAAU,UAAU;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,kFAAkF;IAClF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AA2CD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAMpF;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,UAAU,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GACzC,OAAO,CAAC,mBAAmB,CAAC,CAwC9B"}
|
package/dist/admin-domain.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MPL-2.0
|
|
2
2
|
/**
|
|
3
3
|
* `cms-provision admin-domain enable` — serve a gcp-firebase admin at
|
|
4
|
-
* `admin.<domain>` instead of its `*.run.app` URL
|
|
4
|
+
* `admin.<domain>` instead of its `*.run.app` URL — and, the same way,
|
|
5
|
+
* `cms-provision staging-domain enable` (#650): serve the protected staging
|
|
6
|
+
* site at `staging.<domain>` from the gateway, which gates and proxies it.
|
|
5
7
|
*
|
|
6
8
|
* The gcp-firebase stack only creates that Cloud Run domain mapping behind
|
|
7
9
|
* `provisionAdminDomain=true`, off by default: Google refuses a mapping
|
|
@@ -26,6 +28,7 @@
|
|
|
26
28
|
* from the start.
|
|
27
29
|
*/
|
|
28
30
|
import { gcloud as defaultGcloud } from "./gcloud.js";
|
|
31
|
+
import { ensureGcloudComponent } from "./gcloud-components.js";
|
|
29
32
|
function recordsOf(json, hostname) {
|
|
30
33
|
const parsed = JSON.parse(json);
|
|
31
34
|
return (parsed.status?.resourceRecords ?? []).map((r) => ({
|
|
@@ -34,36 +37,105 @@ function recordsOf(json, hostname) {
|
|
|
34
37
|
value: r.rrdata ?? "",
|
|
35
38
|
}));
|
|
36
39
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
function logOf(opts) {
|
|
41
|
+
return opts.log ?? ((message) => console.log(message));
|
|
42
|
+
}
|
|
43
|
+
/** The one deployed Cloud Run service of `slug` (Pulumi-suffixed name). */
|
|
44
|
+
async function findService(run, scope, slug) {
|
|
45
|
+
const prefix = `caelo-production-${slug}`;
|
|
41
46
|
const services = await run([
|
|
42
47
|
"run",
|
|
43
48
|
"services",
|
|
44
49
|
"list",
|
|
45
50
|
...scope,
|
|
46
|
-
|
|
51
|
+
`--filter=metadata.name~^${prefix}`,
|
|
47
52
|
"--format=value(metadata.name)",
|
|
48
53
|
]);
|
|
49
54
|
if (!services.ok) {
|
|
50
|
-
return {
|
|
55
|
+
return { ok: false, error: `list Cloud Run services: ${services.stderr.trim()}` };
|
|
51
56
|
}
|
|
52
57
|
const matches = services.stdout
|
|
53
58
|
.split("\n")
|
|
54
59
|
.map((s) => s.trim())
|
|
55
60
|
.filter(Boolean);
|
|
56
|
-
// Fail closed like the other
|
|
57
|
-
// failed deploy, picking one would bind the domain to the wrong revision.
|
|
61
|
+
// Fail closed like the other service lookups: with a leftover service from
|
|
62
|
+
// a failed deploy, picking one would bind the domain to the wrong revision.
|
|
58
63
|
const service = matches.length === 1 ? matches[0] : undefined;
|
|
59
64
|
if (!service) {
|
|
60
65
|
return {
|
|
61
|
-
|
|
66
|
+
ok: false,
|
|
62
67
|
error: matches.length === 0
|
|
63
|
-
?
|
|
64
|
-
: `expected exactly one
|
|
68
|
+
? `${slug} Cloud Run service not found: no ${prefix}* service`
|
|
69
|
+
: `expected exactly one ${prefix}* Cloud Run service, found ${matches.length} (${matches.join(", ")}); delete the stale one first`,
|
|
65
70
|
};
|
|
66
71
|
}
|
|
72
|
+
return { ok: true, service };
|
|
73
|
+
}
|
|
74
|
+
export async function enableAdminDomain(opts) {
|
|
75
|
+
const run = opts.run ?? defaultGcloud;
|
|
76
|
+
const scope = [`--region=${opts.region}`, `--project=${opts.projectId}`];
|
|
77
|
+
const found = await findService(run, scope, "admin");
|
|
78
|
+
if (!found.ok)
|
|
79
|
+
return { status: "failed", error: found.error };
|
|
80
|
+
return mapDomain(run, logOf(opts), scope, opts.domain, `admin.${opts.domain}`, found.service);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* #650 — map the install's staging host (its setting, default
|
|
84
|
+
* `staging.<domain>`; any FQDN) to the gateway, which serves the protected
|
|
85
|
+
* staging site. Cloud Run refuses domain-mapped requests while a service's
|
|
86
|
+
* ingress is internal-only, so the gateway's ingress is opened first (the
|
|
87
|
+
* stack declares the same since #650; installs from before keep the old
|
|
88
|
+
* value until this runs). Idempotent like the admin mapping.
|
|
89
|
+
*/
|
|
90
|
+
export async function enableStagingDomain(opts) {
|
|
91
|
+
const run = opts.run ?? defaultGcloud;
|
|
92
|
+
const scope = [`--region=${opts.region}`, `--project=${opts.projectId}`];
|
|
93
|
+
const found = await findService(run, scope, "gateway");
|
|
94
|
+
if (!found.ok)
|
|
95
|
+
return { status: "failed", error: found.error };
|
|
96
|
+
const ingress = await run([
|
|
97
|
+
"run",
|
|
98
|
+
"services",
|
|
99
|
+
"describe",
|
|
100
|
+
found.service,
|
|
101
|
+
...scope,
|
|
102
|
+
'--format=value(metadata.annotations["run.googleapis.com/ingress"])',
|
|
103
|
+
]);
|
|
104
|
+
if (!ingress.ok) {
|
|
105
|
+
return { status: "failed", error: `read the gateway's ingress: ${ingress.stderr.trim()}` };
|
|
106
|
+
}
|
|
107
|
+
if (ingress.stdout.trim() !== "all") {
|
|
108
|
+
const update = await run([
|
|
109
|
+
"run",
|
|
110
|
+
"services",
|
|
111
|
+
"update",
|
|
112
|
+
found.service,
|
|
113
|
+
...scope,
|
|
114
|
+
"--ingress=all",
|
|
115
|
+
"--quiet",
|
|
116
|
+
]);
|
|
117
|
+
if (!update.ok) {
|
|
118
|
+
return {
|
|
119
|
+
status: "failed",
|
|
120
|
+
error: `open the gateway's ingress for the domain mapping: ${update.stderr.trim()}`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Search Console verification: the install domain covers its own
|
|
125
|
+
// subdomains; a host on another domain is verified itself.
|
|
126
|
+
const verifyDomain = opts.stagingHost === opts.domain || opts.stagingHost.endsWith(`.${opts.domain}`)
|
|
127
|
+
? opts.domain
|
|
128
|
+
: opts.stagingHost;
|
|
129
|
+
return mapDomain(run, logOf(opts), scope, verifyDomain, opts.stagingHost, found.service);
|
|
130
|
+
}
|
|
131
|
+
/** Steps 2–4 of the module doc for `hostname` → `service`. */
|
|
132
|
+
async function mapDomain(run, log, scope, domain, hostname, service) {
|
|
133
|
+
// Fully managed Cloud Run domain mappings exist only on the beta track: the
|
|
134
|
+
// GA `gcloud run domain-mappings` is Cloud Run for Anthos only. Install the
|
|
135
|
+
// component up front instead of letting gcloud ask for it mid-command.
|
|
136
|
+
const beta = await ensureGcloudComponent("beta", { run, log });
|
|
137
|
+
if (!beta.ok)
|
|
138
|
+
return { status: "failed", error: beta.error };
|
|
67
139
|
const describeArgs = [
|
|
68
140
|
"beta",
|
|
69
141
|
"run",
|
|
@@ -88,15 +160,15 @@ export async function enableAdminDomain(opts) {
|
|
|
88
160
|
return { status: "failed", error: `list verified domains: ${verified.stderr.trim()}` };
|
|
89
161
|
}
|
|
90
162
|
const verifiedIds = verified.stdout.split("\n").map((s) => s.trim());
|
|
91
|
-
if (!verifiedIds.includes(
|
|
92
|
-
const verify = await run(["domains", "verify",
|
|
163
|
+
if (!verifiedIds.includes(domain) && !verifiedIds.includes(hostname)) {
|
|
164
|
+
const verify = await run(["domains", "verify", domain]);
|
|
93
165
|
if (!verify.ok) {
|
|
94
166
|
return {
|
|
95
167
|
status: "failed",
|
|
96
|
-
error: `start domain verification for ${
|
|
168
|
+
error: `start domain verification for ${domain}: ${verify.stderr.trim()}`,
|
|
97
169
|
};
|
|
98
170
|
}
|
|
99
|
-
return { status: "needs-verification", hostname, verifyDomain:
|
|
171
|
+
return { status: "needs-verification", hostname, verifyDomain: domain };
|
|
100
172
|
}
|
|
101
173
|
const create = await run([
|
|
102
174
|
"beta",
|
package/dist/admin-domain.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin-domain.js","sourceRoot":"","sources":["../src/admin-domain.ts"],"names":[],"mappings":"AAAA,mCAAmC;AAEnC
|
|
1
|
+
{"version":3,"file":"admin-domain.js","sourceRoot":"","sources":["../src/admin-domain.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waiting for a Google-managed TLS certificate (§11.C: the CLI polls certs
|
|
3
|
+
* itself instead of telling the operator to check back later).
|
|
4
|
+
*
|
|
5
|
+
* Two certificate sources, one wait loop:
|
|
6
|
+
* - the `gcp` load balancer's managed SSL certificates
|
|
7
|
+
* ({@link lbCertProbe}) — the installer waits for the admin host;
|
|
8
|
+
* - a Cloud Run domain mapping's certificate ({@link domainMappingProbe})
|
|
9
|
+
* — `admin-domain enable` / `staging-domain enable` on gcp-firebase.
|
|
10
|
+
*
|
|
11
|
+
* {@link waitForCert} is pure apart from its injected clock, sleep and
|
|
12
|
+
* probe, so the timeout and progress behaviour is unit-tested without
|
|
13
|
+
* gcloud or real time.
|
|
14
|
+
*/
|
|
15
|
+
import type { GcloudResult } from "./gcloud.js";
|
|
16
|
+
type Run = (args: string[]) => Promise<GcloudResult>;
|
|
17
|
+
/** One look at a certificate. */
|
|
18
|
+
export type CertProbe = {
|
|
19
|
+
readonly state: "ready";
|
|
20
|
+
} | {
|
|
21
|
+
readonly state: "pending";
|
|
22
|
+
readonly detail: string;
|
|
23
|
+
}
|
|
24
|
+
/** gcloud itself failed (auth, permissions) — waiting will not fix it. */
|
|
25
|
+
| {
|
|
26
|
+
readonly state: "error";
|
|
27
|
+
readonly error: string;
|
|
28
|
+
};
|
|
29
|
+
export type CertWaitResult = {
|
|
30
|
+
readonly ok: true;
|
|
31
|
+
} | {
|
|
32
|
+
readonly ok: false;
|
|
33
|
+
readonly reason: "timeout";
|
|
34
|
+
readonly lastDetail: string;
|
|
35
|
+
} | {
|
|
36
|
+
readonly ok: false;
|
|
37
|
+
readonly reason: "error";
|
|
38
|
+
readonly error: string;
|
|
39
|
+
};
|
|
40
|
+
export interface CertWaitOpts {
|
|
41
|
+
readonly probe: () => Promise<CertProbe>;
|
|
42
|
+
readonly timeoutMs: number;
|
|
43
|
+
readonly intervalMs: number;
|
|
44
|
+
/** Called whenever the probe's detail text changes (spinner message). */
|
|
45
|
+
readonly onUpdate?: (detail: string) => void;
|
|
46
|
+
readonly sleep?: (ms: number) => Promise<void>;
|
|
47
|
+
readonly now?: () => number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Poll `probe` until the certificate is ready, gcloud fails, or `timeoutMs`
|
|
51
|
+
* passes. The probe runs once more at the deadline so a certificate that
|
|
52
|
+
* flips during the last interval is not reported as a timeout.
|
|
53
|
+
*/
|
|
54
|
+
export declare function waitForCert(opts: CertWaitOpts): Promise<CertWaitResult>;
|
|
55
|
+
/**
|
|
56
|
+
* The certificate state of the Cloud Run domain mapping for `hostname`.
|
|
57
|
+
* Ready once the mapping's `Ready` condition is True (Google sets it after
|
|
58
|
+
* `CertificateProvisioned` and `DomainRoutable`); until then the detail is
|
|
59
|
+
* the first non-True condition's message, which says what Google waits for
|
|
60
|
+
* (usually the DNS record).
|
|
61
|
+
*/
|
|
62
|
+
export declare function domainMappingProbe(run: Run, target: {
|
|
63
|
+
readonly hostname: string;
|
|
64
|
+
readonly region: string;
|
|
65
|
+
readonly projectId: string;
|
|
66
|
+
}): Promise<CertProbe>;
|
|
67
|
+
/**
|
|
68
|
+
* Whether the `gcp` load balancer has an ACTIVE managed certificate that
|
|
69
|
+
* covers `hostname`. Any such certificate counts: installs from before the
|
|
70
|
+
* admin got its own certificate serve the admin from the combined one.
|
|
71
|
+
* A certificate is only served once its overall status is ACTIVE, so a
|
|
72
|
+
* combined certificate whose other domain still fails does not count.
|
|
73
|
+
*/
|
|
74
|
+
export declare function lbCertProbe(run: Run, target: {
|
|
75
|
+
readonly projectId: string;
|
|
76
|
+
readonly hostname: string;
|
|
77
|
+
}): Promise<CertProbe>;
|
|
78
|
+
/**
|
|
79
|
+
* How long the CLI waits for a certificate before handing the operator the
|
|
80
|
+
* status command. Google documents up to 60 min after DNS resolves.
|
|
81
|
+
*/
|
|
82
|
+
export declare const CERT_WAIT_TIMEOUT_MS: number;
|
|
83
|
+
export {};
|
|
84
|
+
//# sourceMappingURL=cert-wait.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cert-wait.d.ts","sourceRoot":"","sources":["../src/cert-wait.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;AAErD,iCAAiC;AACjC,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC3B;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACxD,0EAA0E;GACxE;IAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;CAAE,GACrB;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC/E;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAiB7E;AASD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,GAAG,EACR,MAAM,EAAE;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACzF,OAAO,CAAC,SAAS,CAAC,CAwBpB;AAOD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,GAAG,EACR,MAAM,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAChE,OAAO,CAAC,SAAS,CAAC,CA+BpB;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,QAAiB,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Poll `probe` until the certificate is ready, gcloud fails, or `timeoutMs`
|
|
4
|
+
* passes. The probe runs once more at the deadline so a certificate that
|
|
5
|
+
* flips during the last interval is not reported as a timeout.
|
|
6
|
+
*/
|
|
7
|
+
export async function waitForCert(opts) {
|
|
8
|
+
const now = opts.now ?? Date.now;
|
|
9
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
10
|
+
const deadline = now() + opts.timeoutMs;
|
|
11
|
+
let lastDetail = "";
|
|
12
|
+
for (;;) {
|
|
13
|
+
const p = await opts.probe();
|
|
14
|
+
if (p.state === "ready")
|
|
15
|
+
return { ok: true };
|
|
16
|
+
if (p.state === "error")
|
|
17
|
+
return { ok: false, reason: "error", error: p.error };
|
|
18
|
+
if (p.detail !== lastDetail) {
|
|
19
|
+
lastDetail = p.detail;
|
|
20
|
+
opts.onUpdate?.(p.detail);
|
|
21
|
+
}
|
|
22
|
+
const remaining = deadline - now();
|
|
23
|
+
if (remaining <= 0)
|
|
24
|
+
return { ok: false, reason: "timeout", lastDetail };
|
|
25
|
+
await sleep(Math.min(opts.intervalMs, remaining));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The certificate state of the Cloud Run domain mapping for `hostname`.
|
|
30
|
+
* Ready once the mapping's `Ready` condition is True (Google sets it after
|
|
31
|
+
* `CertificateProvisioned` and `DomainRoutable`); until then the detail is
|
|
32
|
+
* the first non-True condition's message, which says what Google waits for
|
|
33
|
+
* (usually the DNS record).
|
|
34
|
+
*/
|
|
35
|
+
export async function domainMappingProbe(run, target) {
|
|
36
|
+
const r = await run([
|
|
37
|
+
"beta",
|
|
38
|
+
"run",
|
|
39
|
+
"domain-mappings",
|
|
40
|
+
"describe",
|
|
41
|
+
`--domain=${target.hostname}`,
|
|
42
|
+
`--region=${target.region}`,
|
|
43
|
+
`--project=${target.projectId}`,
|
|
44
|
+
"--format=json",
|
|
45
|
+
]);
|
|
46
|
+
if (!r.ok)
|
|
47
|
+
return { state: "error", error: `describe the domain mapping: ${r.stderr.trim()}` };
|
|
48
|
+
const conditions = JSON.parse(r.stdout).status?.conditions ??
|
|
49
|
+
[];
|
|
50
|
+
const ready = conditions.find((c) => c.type === "Ready");
|
|
51
|
+
if (ready?.status === "True")
|
|
52
|
+
return { state: "ready" };
|
|
53
|
+
const blocking = conditions.find((c) => c.type === "CertificateProvisioned" && c.status !== "True") ??
|
|
54
|
+
conditions.find((c) => c.status !== "True");
|
|
55
|
+
const detail = blocking
|
|
56
|
+
? `${blocking.type}: ${blocking.message || blocking.reason || blocking.status || "pending"}`
|
|
57
|
+
: "Google has not reported the mapping's status yet";
|
|
58
|
+
return { state: "pending", detail };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Whether the `gcp` load balancer has an ACTIVE managed certificate that
|
|
62
|
+
* covers `hostname`. Any such certificate counts: installs from before the
|
|
63
|
+
* admin got its own certificate serve the admin from the combined one.
|
|
64
|
+
* A certificate is only served once its overall status is ACTIVE, so a
|
|
65
|
+
* combined certificate whose other domain still fails does not count.
|
|
66
|
+
*/
|
|
67
|
+
export async function lbCertProbe(run, target) {
|
|
68
|
+
const r = await run([
|
|
69
|
+
"compute",
|
|
70
|
+
"ssl-certificates",
|
|
71
|
+
"list",
|
|
72
|
+
"--global",
|
|
73
|
+
`--project=${target.projectId}`,
|
|
74
|
+
"--format=json",
|
|
75
|
+
]);
|
|
76
|
+
if (!r.ok)
|
|
77
|
+
return { state: "error", error: `list SSL certificates: ${r.stderr.trim()}` };
|
|
78
|
+
const covering = JSON.parse(r.stdout).filter((c) => c.managed?.domainStatus?.[target.hostname] !== undefined);
|
|
79
|
+
if (covering.some((c) => c.managed?.status === "ACTIVE" && c.managed.domainStatus?.[target.hostname] === "ACTIVE")) {
|
|
80
|
+
return { state: "ready" };
|
|
81
|
+
}
|
|
82
|
+
if (covering.length === 0) {
|
|
83
|
+
return { state: "pending", detail: `no managed certificate lists ${target.hostname} yet` };
|
|
84
|
+
}
|
|
85
|
+
const detail = covering
|
|
86
|
+
.map((c) => `${c.name ?? "certificate"}: ${target.hostname} ${c.managed?.domainStatus?.[target.hostname]}`)
|
|
87
|
+
.join("; ");
|
|
88
|
+
return { state: "pending", detail };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* How long the CLI waits for a certificate before handing the operator the
|
|
92
|
+
* status command. Google documents up to 60 min after DNS resolves.
|
|
93
|
+
*/
|
|
94
|
+
export const CERT_WAIT_TIMEOUT_MS = 60 * 60 * 1000;
|
|
95
|
+
//# sourceMappingURL=cert-wait.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cert-wait.js","sourceRoot":"","sources":["../src/cert-wait.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** One place a JSON value holds secret material. Never carries the secret itself. */
|
|
2
|
+
export interface SecretFinding {
|
|
3
|
+
/** Dotted path to the offending value, e.g. `steps.pulumi-up-acme.outputs.adminDatabaseUrlOut`. */
|
|
4
|
+
readonly path: string;
|
|
5
|
+
/**
|
|
6
|
+
* `credential-url`: a URL whose user-info carries a password.
|
|
7
|
+
* `token-url`: a URL with a token / secret / password query parameter.
|
|
8
|
+
* `secret-key`: a key whose name marks its value as secret.
|
|
9
|
+
*/
|
|
10
|
+
readonly kind: "credential-url" | "token-url" | "secret-key";
|
|
11
|
+
/** For a credential URL: the user whose password it carries. */
|
|
12
|
+
readonly user?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Every place `value` holds secret material.
|
|
16
|
+
*
|
|
17
|
+
* @param value any JSON-shaped value (a checkpoint payload, install.json)
|
|
18
|
+
* @param path the path prefix to report findings under
|
|
19
|
+
*/
|
|
20
|
+
export declare function findSecrets(value: unknown, path?: string): SecretFinding[];
|
|
21
|
+
/**
|
|
22
|
+
* Throw when `value` holds secret material. The message names the paths,
|
|
23
|
+
* never the values, so the error itself cannot leak them.
|
|
24
|
+
*
|
|
25
|
+
* @param what what was about to be written, for the message
|
|
26
|
+
*/
|
|
27
|
+
export declare function assertNoSecrets(value: unknown, what: string): void;
|
|
28
|
+
/** {@link assertNoSecrets} for a whole checkpoint (see {@link findCheckpointSecrets}). */
|
|
29
|
+
export declare function assertNoCheckpointSecrets(checkpoint: unknown, what: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* {@link findSecrets} for a progress.json checkpoint. Its step names are
|
|
32
|
+
* derived from operator input (`project-<project id>`), so they are
|
|
33
|
+
* identifiers, not field names: a project called `acme-token` must not read
|
|
34
|
+
* as a secret. Only the step payloads (and the other top-level fields) are
|
|
35
|
+
* inspected.
|
|
36
|
+
*/
|
|
37
|
+
export declare function findCheckpointSecrets(checkpoint: unknown): SecretFinding[];
|
|
38
|
+
/**
|
|
39
|
+
* `value` without its secret material: secret-named keys and strings that
|
|
40
|
+
* hold a credential or token URL are dropped. Everything else is kept.
|
|
41
|
+
*/
|
|
42
|
+
export declare function withoutSecrets(value: unknown): unknown;
|
|
43
|
+
/**
|
|
44
|
+
* The Pulumi outputs worth checkpointing: everything except outputs Pulumi
|
|
45
|
+
* marks secret and anything else that holds secret material (the Owner
|
|
46
|
+
* setup link). Nothing reads secrets back from the checkpoint; code that
|
|
47
|
+
* needs one reads the stack (`readStackOutput`) when it needs it.
|
|
48
|
+
*
|
|
49
|
+
* @param outputs the stack outputs as the Automation API returns them
|
|
50
|
+
*/
|
|
51
|
+
export declare function checkpointableOutputs(outputs: Readonly<Record<string, {
|
|
52
|
+
value: unknown;
|
|
53
|
+
secret?: boolean;
|
|
54
|
+
}>>): Record<string, unknown>;
|
|
55
|
+
/** Outcome of {@link scrubProgressFile}. */
|
|
56
|
+
export interface ScrubReport {
|
|
57
|
+
/** What was removed (paths + kinds, never values). Empty when the file was clean or absent. */
|
|
58
|
+
readonly removed: readonly SecretFinding[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Remove secret material from a `progress.json` in place. The rewrite is
|
|
62
|
+
* atomic (temp file in the same directory, then rename) and the result is
|
|
63
|
+
* mode 0600, so an interrupted scrub never leaves a truncated checkpoint
|
|
64
|
+
* and wizard re-runs still find every completed step.
|
|
65
|
+
*
|
|
66
|
+
* @param path the checkpoint file; a missing file is a no-op
|
|
67
|
+
*/
|
|
68
|
+
export declare function scrubProgressFile(path: string): ScrubReport;
|
|
69
|
+
/**
|
|
70
|
+
* Write `value` as JSON to `path` atomically with mode 0600: a temp file in
|
|
71
|
+
* the same directory (so the rename stays on one filesystem), then rename.
|
|
72
|
+
*/
|
|
73
|
+
export declare function writeJsonAtomic(path: string, value: unknown): void;
|
|
74
|
+
/**
|
|
75
|
+
* What the operator should do about secrets a scrub removed: one line per
|
|
76
|
+
* affected secret, with the exact command where one exists.
|
|
77
|
+
*
|
|
78
|
+
* @param removed the scrub's findings
|
|
79
|
+
* @param installId the install, named in each command so it acts on the right one
|
|
80
|
+
*/
|
|
81
|
+
export declare function rotationHints(removed: readonly SecretFinding[], installId: string): string[];
|
|
82
|
+
/**
|
|
83
|
+
* `text` with every URL password masked (`postgresql://admin_role:***@`),
|
|
84
|
+
* for anything the CLI prints that could carry one (errors from Pulumi or
|
|
85
|
+
* gcloud, for instance). Same masking as the API gateway's error log.
|
|
86
|
+
*/
|
|
87
|
+
export declare function redactCredentials(text: string): string;
|
|
88
|
+
//# sourceMappingURL=checkpoint-secrets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint-secrets.d.ts","sourceRoot":"","sources":["../src/checkpoint-secrets.ts"],"names":[],"mappings":"AA4BA,qFAAqF;AACrF,MAAM,WAAW,aAAa;IAC5B,mGAAmG;IACnG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,WAAW,GAAG,YAAY,CAAC;IAC7D,gEAAgE;IAChE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AA2CD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,SAAK,GAAG,aAAa,EAAE,CAetE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAElE;AAED,0FAA0F;AAC1F,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAEjF;AAcD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,OAAO,GAAG,aAAa,EAAE,CAO1E;AAcD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAgBtD;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC,GACtE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB;AAED,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,+FAA+F;IAC/F,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAO3D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CASlE;AAID;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAuB5F;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD"}
|