@intentius/chant-lexicon-gcp 0.124.37 → 0.124.39
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/api/read-client.d.ts +14 -1
- package/dist/api/read-client.d.ts.map +1 -1
- package/dist/api/read-client.js +25 -10
- package/dist/composites/catalog.d.ts.map +1 -1
- package/dist/composites/catalog.js +187 -0
- package/dist/composites/index.d.ts +4 -0
- package/dist/composites/index.d.ts.map +1 -1
- package/dist/composites/index.js +2 -0
- package/dist/composites/private-egress-network.d.ts +42 -0
- package/dist/composites/private-egress-network.d.ts.map +1 -0
- package/dist/composites/private-egress-network.js +48 -0
- package/dist/composites/public-cloud-run.d.ts +85 -0
- package/dist/composites/public-cloud-run.d.ts.map +1 -0
- package/dist/composites/public-cloud-run.js +144 -0
- package/dist/deep-observe.d.ts +5 -0
- package/dist/deep-observe.d.ts.map +1 -1
- package/dist/deep-observe.js +37 -3
- package/dist/describe-resources.d.ts +13 -0
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/describe-resources.js +19 -0
- package/dist/health.d.ts +26 -2
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js +85 -6
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/integrity.json +3 -3
- package/dist/manifest.json +1 -1
- package/dist/op/activities/gcp-apply.d.ts +19 -0
- package/dist/op/activities/gcp-apply.d.ts.map +1 -1
- package/dist/op/activities/gcp-apply.js +16 -0
- package/dist/op/activities/gcp-compute.d.ts +9 -0
- package/dist/op/activities/gcp-compute.d.ts.map +1 -1
- package/dist/op/activities/gcp-compute.js +107 -11
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +6 -2
- package/dist/skills/chant-gcp.md +2 -0
- package/package.json +2 -2
- package/src/api/read-client.ts +30 -9
- package/src/composites/catalog.ts +187 -0
- package/src/composites/cloud-run-lb.test.ts +93 -0
- package/src/composites/index.ts +4 -0
- package/src/composites/private-egress-network.ts +82 -0
- package/src/composites/public-cloud-run.ts +245 -0
- package/src/deep-observe.test.ts +46 -0
- package/src/deep-observe.ts +41 -3
- package/src/describe-resources.ts +23 -1
- package/src/health.test.ts +65 -1
- package/src/health.ts +88 -11
- package/src/index.ts +2 -1
- package/src/op/activities/gcp-apply.ts +31 -1
- package/src/op/activities/gcp-compute-reconcile.test.ts +150 -0
- package/src/op/activities/gcp-compute.test.ts +4 -2
- package/src/op/activities/gcp-compute.ts +129 -11
- package/src/plugin.ts +6 -2
- package/src/project-config.test.ts +50 -3
- package/src/read-refs.test.ts +60 -0
- package/src/skills/chant-gcp.md +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcp-apply.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-apply.ts"],"names":[],"mappings":"AAgBA,OAAO,EAEL,KAAK,WAAW,EAGjB,MAAM,wBAAwB,CAAC;AAIhC;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEvF;AAQD,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACtC,CAAC;IACF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,8DAA8D;AAC9D,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,wBAAwB,CAAC,EAAE,OAAO,CAAC;QACnC,UAAU,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QACnC,aAAa,CAAC,EAAE,KAAK,CAAC;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,CAAC,CAAC;KAClG,CAAC;CACH;AAED,gEAAgE;AAChE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE;QAAE,wBAAwB,EAAE;YAAE,OAAO,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE,CAAC;IACtE,UAAU,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAClC,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,KAAK,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;CACvG;AAED,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAID;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/D,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,OAAO,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"gcp-apply.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -485,6 +485,22 @@ export async function applyResource(mapper, resource, ctx, http = defaultHttp, s
|
|
|
485
485
|
}
|
|
486
486
|
const plan = mapper.plan(resource, ctx);
|
|
487
487
|
const get = await http("GET", plan.getUrl, undefined, signal);
|
|
488
|
+
if (get.status === 200 && plan.reconcile) {
|
|
489
|
+
const step = await plan.reconcile(parseJson(get.text), http, signal);
|
|
490
|
+
if ("replace" in step) {
|
|
491
|
+
// Reported, not attempted: replacing deletes the resource, which apply never does on its own.
|
|
492
|
+
console.log(`replace required: ${mapper.kind}/${name}: ${step.replace}`);
|
|
493
|
+
return { kind: mapper.kind, name, created: false, updated: false, replaceRequired: step.replace };
|
|
494
|
+
}
|
|
495
|
+
for (const req of step.requests) {
|
|
496
|
+
const res = await http(req.method, req.url, req.body, signal);
|
|
497
|
+
if (res.status >= 300) {
|
|
498
|
+
throw new Error(`${mapper.kind} ${name} update failed (${res.status}): ${res.text}`);
|
|
499
|
+
}
|
|
500
|
+
await pollIfAsync(mapper, res.text, ctx, http, signal);
|
|
501
|
+
}
|
|
502
|
+
return { kind: mapper.kind, name, created: false, updated: step.requests.length > 0 };
|
|
503
|
+
}
|
|
488
504
|
if (get.status === 200) {
|
|
489
505
|
// Exists. Reconcile to desired if the mapper supports it; otherwise leave it.
|
|
490
506
|
if (!plan.update) {
|
|
@@ -54,6 +54,15 @@ export declare function forwardingRuleBody(resource: GcpResource, ctx: ComputeCt
|
|
|
54
54
|
* resource itself has nothing to poll.
|
|
55
55
|
*/
|
|
56
56
|
export declare const computeOperation: OperationSpec;
|
|
57
|
+
/**
|
|
58
|
+
* True when the live value holds the desired one: every desired key is set
|
|
59
|
+
* live to an equal value, arrays element by element, and a compute URL
|
|
60
|
+
* equals its partial form. Fields only the live side has (defaults, server
|
|
61
|
+
* ids) don't count. Pure.
|
|
62
|
+
*/
|
|
63
|
+
export declare function holds(desired: unknown, live: unknown): boolean;
|
|
64
|
+
/** The desired body's top-level fields the live resource doesn't hold, `name` aside. Pure. */
|
|
65
|
+
export declare function changedFields(desired: Obj, live: Obj): string[];
|
|
57
66
|
export declare const computeAddressMapper: ResourceMapper;
|
|
58
67
|
export declare const regionNetworkEndpointGroupMapper: ResourceMapper;
|
|
59
68
|
export declare const backendServiceMapper: ResourceMapper;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcp-compute.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-compute.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gcp-compute.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-compute.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,9 +14,16 @@
|
|
|
14
14
|
* backend service can point at a regional NEG.
|
|
15
15
|
*
|
|
16
16
|
* Inserts return a compute `Operation`, polled at its `selfLink` (rebased onto
|
|
17
|
-
* the endpoint override, so floci-gcp answers).
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* the endpoint override, so floci-gcp answers).
|
|
18
|
+
*
|
|
19
|
+
* An existing load balancer resource is compared with its desired body and
|
|
20
|
+
* updated in place where compute allows it (#4238): a backend service or URL
|
|
21
|
+
* map by PATCH with the live `fingerprint`, a proxy's URL map by `setUrlMap`,
|
|
22
|
+
* a forwarding rule's target by `setTarget`, labels by `setLabels` with the
|
|
23
|
+
* live `labelFingerprint`. A difference on a field compute can't change in
|
|
24
|
+
* place (anything on a serverless NEG, an address's IP) is reported as
|
|
25
|
+
* "replace required" and left alone. A network and a subnetwork still
|
|
26
|
+
* reconcile by existence only.
|
|
20
27
|
*/
|
|
21
28
|
import { GCP_RESOURCE_OWNERSHIP_KEYS } from "../../ownership.js";
|
|
22
29
|
import { OWNERSHIP_MANAGED_BY_VALUE } from "@intentius/chant/ownership";
|
|
@@ -326,7 +333,83 @@ export const computeOperation = {
|
|
|
326
333
|
return errors?.length ? errors.map((e) => e.message ?? e.code ?? "error").join("; ") : undefined;
|
|
327
334
|
},
|
|
328
335
|
};
|
|
329
|
-
|
|
336
|
+
/** A compute reference with everything before `projects/` dropped, so a full URL and a partial one compare equal. */
|
|
337
|
+
function refTail(v) {
|
|
338
|
+
const at = v.indexOf("projects/");
|
|
339
|
+
return at >= 0 ? v.slice(at) : v;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* True when the live value holds the desired one: every desired key is set
|
|
343
|
+
* live to an equal value, arrays element by element, and a compute URL
|
|
344
|
+
* equals its partial form. Fields only the live side has (defaults, server
|
|
345
|
+
* ids) don't count. Pure.
|
|
346
|
+
*/
|
|
347
|
+
export function holds(desired, live) {
|
|
348
|
+
if (typeof desired === "string" && typeof live === "string") {
|
|
349
|
+
return desired === live || (desired.includes("projects/") && refTail(desired) === refTail(live));
|
|
350
|
+
}
|
|
351
|
+
if (Array.isArray(desired)) {
|
|
352
|
+
return Array.isArray(live) && desired.length === live.length && desired.every((d, i) => holds(d, live[i]));
|
|
353
|
+
}
|
|
354
|
+
if (isObj(desired)) {
|
|
355
|
+
return isObj(live) && Object.entries(desired).every(([k, v]) => holds(v, live[k]));
|
|
356
|
+
}
|
|
357
|
+
return desired === live;
|
|
358
|
+
}
|
|
359
|
+
/** The desired body's top-level fields the live resource doesn't hold, `name` aside. Pure. */
|
|
360
|
+
export function changedFields(desired, live) {
|
|
361
|
+
return Object.keys(desired).filter((k) => k !== "name" && !holds(desired[k], live[k]));
|
|
362
|
+
}
|
|
363
|
+
const replace = (kind, fields) => ({
|
|
364
|
+
replace: `${fields.join(", ")} differ${fields.length === 1 ? "s" : ""}, and compute can't change ${fields.length === 1 ? "it" : "them"} on a ${kind} in place`,
|
|
365
|
+
});
|
|
366
|
+
/** `labels` by `setLabels` with the live `labelFingerprint`; anything else changed is a replace. */
|
|
367
|
+
function labelsOnly(kind) {
|
|
368
|
+
return async ({ url, desired, live, changed }) => {
|
|
369
|
+
const other = changed.filter((f) => f !== "labels");
|
|
370
|
+
if (other.length)
|
|
371
|
+
return replace(kind, other);
|
|
372
|
+
if (!changed.length)
|
|
373
|
+
return { requests: [] };
|
|
374
|
+
return { requests: [{ method: "POST", url: `${url}/setLabels`, body: { labels: desired.labels, labelFingerprint: live.labelFingerprint } }] };
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
/** Every changed field by one PATCH carrying the live `fingerprint` (backend services, URL maps). */
|
|
378
|
+
const patchWithFingerprint = async ({ url, desired, live, changed }) => changed.length ? { requests: [{ method: "PATCH", url, body: { ...desired, fingerprint: live.fingerprint } }] } : { requests: [] };
|
|
379
|
+
const reconcileProxy = async ({ url, desired, changed }) => {
|
|
380
|
+
const other = changed.filter((f) => f !== "urlMap");
|
|
381
|
+
if (other.length)
|
|
382
|
+
return replace("ComputeTargetHTTPProxy", other);
|
|
383
|
+
return changed.length ? { requests: [{ method: "POST", url: `${url}/setUrlMap`, body: { urlMap: desired.urlMap } }] } : { requests: [] };
|
|
384
|
+
};
|
|
385
|
+
/**
|
|
386
|
+
* A forwarding rule's `IPAddress` is a declared address's partial URL, where
|
|
387
|
+
* the live rule holds the IP itself, so the address is read and its IP
|
|
388
|
+
* compared. The target changes by `setTarget`, labels by `setLabels`; any
|
|
389
|
+
* other field is a replace.
|
|
390
|
+
*/
|
|
391
|
+
const reconcileForwardingRule = async ({ url, desired, live, changed, ctx, http, signal }) => {
|
|
392
|
+
let fields = changed;
|
|
393
|
+
const ip = desired.IPAddress;
|
|
394
|
+
if (fields.includes("IPAddress") && typeof ip === "string" && ip.includes("/addresses/")) {
|
|
395
|
+
const res = await http("GET", `${ctx.base}/compute/v1/${refTail(ip)}`, undefined, signal);
|
|
396
|
+
const address = res.status === 200 ? JSON.parse(res.text).address : undefined;
|
|
397
|
+
if (address !== undefined && address === live.IPAddress)
|
|
398
|
+
fields = fields.filter((f) => f !== "IPAddress");
|
|
399
|
+
}
|
|
400
|
+
const other = fields.filter((f) => f !== "target" && f !== "labels");
|
|
401
|
+
if (other.length)
|
|
402
|
+
return replace("ComputeForwardingRule", other);
|
|
403
|
+
const requests = [];
|
|
404
|
+
if (fields.includes("target"))
|
|
405
|
+
requests.push({ method: "POST", url: `${url}/setTarget`, body: { target: desired.target } });
|
|
406
|
+
if (fields.includes("labels")) {
|
|
407
|
+
requests.push({ method: "POST", url: `${url}/setLabels`, body: { labels: desired.labels, labelFingerprint: live.labelFingerprint } });
|
|
408
|
+
}
|
|
409
|
+
return { requests };
|
|
410
|
+
};
|
|
411
|
+
const replaceOnAnyChange = (kind) => async ({ changed }) => (changed.length ? replace(kind, changed) : { requests: [] });
|
|
412
|
+
function computeMapper(kind, collection, build, regionalCollection = collection, scopeFor = (spec) => scopeOf(spec), reconcile) {
|
|
330
413
|
return {
|
|
331
414
|
kind,
|
|
332
415
|
defaultHost: COMPUTE_HOST,
|
|
@@ -335,15 +418,28 @@ function computeMapper(kind, collection, build, regionalCollection = collection,
|
|
|
335
418
|
const scope = scopeFor(resource.spec);
|
|
336
419
|
const coll = `${ctx.base}/compute/v1/${scopePath(ctx.project, scope)}/${scope === "global" ? collection : regionalCollection}`;
|
|
337
420
|
const name = computeName(resource);
|
|
338
|
-
|
|
421
|
+
const url = `${coll}/${enc(name)}`;
|
|
422
|
+
const desired = build(resource, ctx);
|
|
423
|
+
return {
|
|
424
|
+
getUrl: url,
|
|
425
|
+
create: { method: "POST", url: coll, body: desired },
|
|
426
|
+
...(reconcile
|
|
427
|
+
? {
|
|
428
|
+
reconcile: (live, http, signal) => {
|
|
429
|
+
const l = isObj(live) ? live : {};
|
|
430
|
+
return reconcile({ url, desired, live: l, changed: changedFields(desired, l), ctx, http, signal });
|
|
431
|
+
},
|
|
432
|
+
}
|
|
433
|
+
: {}),
|
|
434
|
+
};
|
|
339
435
|
},
|
|
340
436
|
};
|
|
341
437
|
}
|
|
342
|
-
export const computeAddressMapper = computeMapper("ComputeAddress", "addresses", addressBody);
|
|
343
|
-
export const regionNetworkEndpointGroupMapper = computeMapper("ComputeRegionNetworkEndpointGroup", "networkEndpointGroups", negBody);
|
|
344
|
-
export const backendServiceMapper = computeMapper("ComputeBackendService", "backendServices", backendServiceBody);
|
|
345
|
-
export const urlMapMapper = computeMapper("ComputeURLMap", "urlMaps", urlMapBody);
|
|
346
|
-
export const targetHttpProxyMapper = computeMapper("ComputeTargetHTTPProxy", "targetHttpProxies", targetHttpProxyBody);
|
|
347
|
-
export const forwardingRuleMapper = computeMapper("ComputeForwardingRule", "forwardingRules", forwardingRuleBody);
|
|
438
|
+
export const computeAddressMapper = computeMapper("ComputeAddress", "addresses", addressBody, undefined, undefined, labelsOnly("ComputeAddress"));
|
|
439
|
+
export const regionNetworkEndpointGroupMapper = computeMapper("ComputeRegionNetworkEndpointGroup", "networkEndpointGroups", negBody, undefined, undefined, replaceOnAnyChange("ComputeRegionNetworkEndpointGroup"));
|
|
440
|
+
export const backendServiceMapper = computeMapper("ComputeBackendService", "backendServices", backendServiceBody, undefined, undefined, patchWithFingerprint);
|
|
441
|
+
export const urlMapMapper = computeMapper("ComputeURLMap", "urlMaps", urlMapBody, undefined, undefined, patchWithFingerprint);
|
|
442
|
+
export const targetHttpProxyMapper = computeMapper("ComputeTargetHTTPProxy", "targetHttpProxies", targetHttpProxyBody, undefined, undefined, reconcileProxy);
|
|
443
|
+
export const forwardingRuleMapper = computeMapper("ComputeForwardingRule", "forwardingRules", forwardingRuleBody, undefined, undefined, reconcileForwardingRule);
|
|
348
444
|
export const networkMapper = computeMapper("ComputeNetwork", "networks", networkBody, "networks", () => "global");
|
|
349
445
|
export const subnetworkMapper = computeMapper("ComputeSubnetwork", "subnetworks", subnetworkBody);
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAqC,MAAM,0BAA0B,CAAC;AAuBjG,eAAO,MAAM,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAqC,MAAM,0BAA0B,CAAC;AAuBjG,eAAO,MAAM,SAAS,EAAE,aAyYvB,CAAC"}
|
package/dist/plugin.js
CHANGED
|
@@ -368,8 +368,12 @@ export const bucket = new StorageBucket({
|
|
|
368
368
|
return observeResourcesDeepGcp(options);
|
|
369
369
|
},
|
|
370
370
|
deepNormalizationHooks: gcpDeepNormalizationHooks,
|
|
371
|
-
/**
|
|
372
|
-
|
|
371
|
+
/**
|
|
372
|
+
* Runtime health (#4177, #4239): Cloud Run services, backend services and IAM
|
|
373
|
+
* policy members. Implementation in ./health.ts, whose GCP_HEALTH_TYPES this
|
|
374
|
+
* list matches; written out so the build never imports the reader.
|
|
375
|
+
*/
|
|
376
|
+
healthTypes: ["GCP::Run::Service", "GCP::Compute::BackendService", "GCP::Iam::PolicyMember"],
|
|
373
377
|
async observeHealth(options) {
|
|
374
378
|
const { observeHealthGcp } = await import("./health.js");
|
|
375
379
|
return observeHealthGcp(options);
|
package/dist/skills/chant-gcp.md
CHANGED
|
@@ -202,6 +202,8 @@ Composites group related Config Connector resources with secure defaults. Each r
|
|
|
202
202
|
| `ManagedCertificate` | ComputeManagedSslCertificate | Auto-renewal via Google-managed cert |
|
|
203
203
|
| `SecureProject` | Project + IAMAuditConfig + essential Service resources | Audit logging, required APIs enabled, org policy constraints |
|
|
204
204
|
| `GpuNodePool` | ContainerNodePool with GPU accelerators | Scale-to-zero, nvidia.com/gpu taint, GKE metadata workload identity |
|
|
205
|
+
| `PrivateEgressNetwork` | ComputeNetwork + ComputeSubnetwork | Custom mode, regional routing, Private Google Access |
|
|
206
|
+
| `PublicCloudRun` | IAMServiceAccount + RunService + serverless NEG + global external Application Load Balancer | Own service account, Direct VPC egress for private ranges, optional managed certificate with HTTP to HTTPS redirect, optional Cloud Armor policy |
|
|
205
207
|
|
|
206
208
|
### Example: GKE cluster with secure defaults
|
|
207
209
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-gcp",
|
|
3
|
-
"version": "0.124.
|
|
3
|
+
"version": "0.124.39",
|
|
4
4
|
"description": "Google Cloud lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -76,6 +76,6 @@
|
|
|
76
76
|
"typescript": "^5.9.3"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
|
-
"@intentius/chant": "^0.124.
|
|
79
|
+
"@intentius/chant": "^0.124.39"
|
|
80
80
|
}
|
|
81
81
|
}
|
package/src/api/read-client.ts
CHANGED
|
@@ -47,6 +47,12 @@ export interface GcpReadClientOptions {
|
|
|
47
47
|
project: string;
|
|
48
48
|
/** Injectable HTTP, so tests never touch the network. */
|
|
49
49
|
http?: GcpReadHttp;
|
|
50
|
+
/**
|
|
51
|
+
* The declared resources, refs resolved, as apply passes them (#4251): an
|
|
52
|
+
* IAMPolicyMember on a RunService named by `name` takes the service's region
|
|
53
|
+
* from its declaration, which a read otherwise can't know.
|
|
54
|
+
*/
|
|
55
|
+
manifest?: readonly GcpResource[];
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
/** Injectable HTTP client — mirrors the applier's `GcpHttp`. A body is sent only for a POST read (`:getIamPolicy`). */
|
|
@@ -92,6 +98,28 @@ export function mapperForKind(kind: string): ResourceMapper | undefined {
|
|
|
92
98
|
* build a URL — the same input the applier hands it. Returns the parsed body,
|
|
93
99
|
* or throws {@link GcpReadError} carrying the status.
|
|
94
100
|
*/
|
|
101
|
+
/**
|
|
102
|
+
* The URL a read of this resource GETs: the applier's own `plan().getUrl`, so
|
|
103
|
+
* a health read's sub-calls (`getHealth`) land on the same resource path.
|
|
104
|
+
* Throws a {@link GcpReadError} when the declared spec can't produce one,
|
|
105
|
+
* which is a hole, never an absence.
|
|
106
|
+
*/
|
|
107
|
+
export function readUrl(client: GcpReadClientOptions, kind: string, name: string, declared?: Record<string, unknown>): string {
|
|
108
|
+
const mapper = mapperForKind(kind);
|
|
109
|
+
if (!mapper) throw new GcpReadError(`no REST mapper for kind ${kind}`);
|
|
110
|
+
const base = (client.endpoint ?? mapper.defaultHost).replace(/\/$/, "");
|
|
111
|
+
const resource: GcpResource = {
|
|
112
|
+
kind,
|
|
113
|
+
metadata: { name },
|
|
114
|
+
spec: (declared?.spec as Record<string, unknown> | undefined) ?? declared ?? {},
|
|
115
|
+
};
|
|
116
|
+
try {
|
|
117
|
+
return mapper.plan(resource, { base, project: client.project, ...(client.manifest ? { manifest: client.manifest } : {}) }).getUrl;
|
|
118
|
+
} catch (err) {
|
|
119
|
+
throw new GcpReadError(`could not build a read URL for ${kind}/${name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
95
123
|
export async function getResource(
|
|
96
124
|
client: GcpReadClientOptions,
|
|
97
125
|
kind: string,
|
|
@@ -108,7 +136,7 @@ export async function getResource(
|
|
|
108
136
|
spec: (declared?.spec as Record<string, unknown> | undefined) ?? declared ?? {},
|
|
109
137
|
};
|
|
110
138
|
|
|
111
|
-
const ctx = { base, project: client.project };
|
|
139
|
+
const ctx = { base, project: client.project, ...(client.manifest ? { manifest: client.manifest } : {}) };
|
|
112
140
|
if (mapper.accepts && !mapper.accepts(resource, ctx)) {
|
|
113
141
|
throw new GcpReadError(`no REST read for ${kind}/${name}: its target has no endpoint chant can read`);
|
|
114
142
|
}
|
|
@@ -122,14 +150,7 @@ export async function getResource(
|
|
|
122
150
|
throw new GcpReadError(`could not read ${kind}/${name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
123
151
|
}
|
|
124
152
|
} else {
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
getUrl = mapper.plan(resource, ctx).getUrl;
|
|
128
|
-
} catch (err) {
|
|
129
|
-
// A mapper that cannot plan from the declared spec cannot produce a URL, so
|
|
130
|
-
// there is nothing to read — a hole, never an absence.
|
|
131
|
-
throw new GcpReadError(`could not build a read URL for ${kind}/${name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
132
|
-
}
|
|
153
|
+
const getUrl = readUrl(client, kind, name, declared);
|
|
133
154
|
try {
|
|
134
155
|
res = await http("GET", getUrl);
|
|
135
156
|
} catch (err) {
|
|
@@ -175,6 +175,12 @@ export const compositeCatalog: CompositeEntry[] = [
|
|
|
175
175
|
"required": false,
|
|
176
176
|
"description": "Environment variables."
|
|
177
177
|
},
|
|
178
|
+
{
|
|
179
|
+
"name": "serviceAccount",
|
|
180
|
+
"type": "string",
|
|
181
|
+
"required": false,
|
|
182
|
+
"description": "Service account the service runs as: an email, or the account id of an IAMServiceAccount in the project."
|
|
183
|
+
},
|
|
178
184
|
{
|
|
179
185
|
"name": "labels",
|
|
180
186
|
"type": "Record<string, string>",
|
|
@@ -955,6 +961,59 @@ export const compositeCatalog: CompositeEntry[] = [
|
|
|
955
961
|
}
|
|
956
962
|
]
|
|
957
963
|
},
|
|
964
|
+
{
|
|
965
|
+
"name": "PrivateEgressNetwork",
|
|
966
|
+
"lexicon": "gcp",
|
|
967
|
+
"description": "Create a PrivateEgressNetwork composite.",
|
|
968
|
+
"bundles": [
|
|
969
|
+
"Subnetwork",
|
|
970
|
+
"VPCNetwork"
|
|
971
|
+
],
|
|
972
|
+
"params": [
|
|
973
|
+
{
|
|
974
|
+
"name": "name",
|
|
975
|
+
"type": "string",
|
|
976
|
+
"required": true,
|
|
977
|
+
"description": "Name prefix: the network is `<name>-vpc`, its subnet `<name>-apps`."
|
|
978
|
+
},
|
|
979
|
+
{
|
|
980
|
+
"name": "region",
|
|
981
|
+
"type": "string",
|
|
982
|
+
"required": true,
|
|
983
|
+
"description": "GCP region of the subnet."
|
|
984
|
+
},
|
|
985
|
+
{
|
|
986
|
+
"name": "subnetCidr",
|
|
987
|
+
"type": "string",
|
|
988
|
+
"required": true,
|
|
989
|
+
"description": "IP CIDR range of the subnet."
|
|
990
|
+
},
|
|
991
|
+
{
|
|
992
|
+
"name": "privateIpGoogleAccess",
|
|
993
|
+
"type": "boolean",
|
|
994
|
+
"required": false,
|
|
995
|
+
"description": "Enable private Google access on the subnet (default: true)."
|
|
996
|
+
},
|
|
997
|
+
{
|
|
998
|
+
"name": "labels",
|
|
999
|
+
"type": "Record<string, string>",
|
|
1000
|
+
"required": false,
|
|
1001
|
+
"description": "Additional labels."
|
|
1002
|
+
},
|
|
1003
|
+
{
|
|
1004
|
+
"name": "namespace",
|
|
1005
|
+
"type": "string",
|
|
1006
|
+
"required": false,
|
|
1007
|
+
"description": "Namespace for all resources."
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
"name": "defaults",
|
|
1011
|
+
"type": "{ vpc?: Partial<ConstructorParameters<typeof VPCNetwork>[0]>; subnet?: Partial<ConstructorParameters<typeof Subnetwor...",
|
|
1012
|
+
"required": false,
|
|
1013
|
+
"description": "Per-member defaults for customizing individual resources."
|
|
1014
|
+
}
|
|
1015
|
+
]
|
|
1016
|
+
},
|
|
958
1017
|
{
|
|
959
1018
|
"name": "PrivateService",
|
|
960
1019
|
"lexicon": "gcp",
|
|
@@ -1033,6 +1092,134 @@ export const compositeCatalog: CompositeEntry[] = [
|
|
|
1033
1092
|
}
|
|
1034
1093
|
]
|
|
1035
1094
|
},
|
|
1095
|
+
{
|
|
1096
|
+
"name": "PublicCloudRun",
|
|
1097
|
+
"lexicon": "gcp",
|
|
1098
|
+
"description": "Create a PublicCloudRun composite.",
|
|
1099
|
+
"bundles": [
|
|
1100
|
+
"BackendService",
|
|
1101
|
+
"CloudRunService",
|
|
1102
|
+
"ComputeAddress",
|
|
1103
|
+
"ForwardingRule",
|
|
1104
|
+
"GCPServiceAccount",
|
|
1105
|
+
"IAMPolicyMember",
|
|
1106
|
+
"ManagedSSLCertificate",
|
|
1107
|
+
"RegionNetworkEndpointGroup",
|
|
1108
|
+
"TargetHTTPProxy",
|
|
1109
|
+
"TargetHTTPSProxy",
|
|
1110
|
+
"URLMap"
|
|
1111
|
+
],
|
|
1112
|
+
"params": [
|
|
1113
|
+
{
|
|
1114
|
+
"name": "name",
|
|
1115
|
+
"type": "string",
|
|
1116
|
+
"required": true,
|
|
1117
|
+
"description": "Name prefix every resource is named with."
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
"name": "image",
|
|
1121
|
+
"type": "string",
|
|
1122
|
+
"required": true,
|
|
1123
|
+
"description": "Container image."
|
|
1124
|
+
},
|
|
1125
|
+
{
|
|
1126
|
+
"name": "region",
|
|
1127
|
+
"type": "string",
|
|
1128
|
+
"required": true,
|
|
1129
|
+
"description": "GCP region of the service and its network endpoint group."
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
"name": "projectId",
|
|
1133
|
+
"type": "string",
|
|
1134
|
+
"required": false,
|
|
1135
|
+
"description": "GCP project, as `projectRef.external` (default: GCP.ProjectId, resolved from GCP_PROJECT_ID at build)."
|
|
1136
|
+
},
|
|
1137
|
+
{
|
|
1138
|
+
"name": "vpc",
|
|
1139
|
+
"type": "{ name: string }",
|
|
1140
|
+
"required": true,
|
|
1141
|
+
"description": "VPC the service's egress goes into, such as a PrivateEgressNetwork's `vpc`."
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
"name": "subnet",
|
|
1145
|
+
"type": "{ name: string }",
|
|
1146
|
+
"required": true,
|
|
1147
|
+
"description": "Subnet the service's egress goes into, such as a PrivateEgressNetwork's `subnet`."
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
"name": "vpcEgress",
|
|
1151
|
+
"type": "string",
|
|
1152
|
+
"required": false,
|
|
1153
|
+
"description": "Egress sent through the VPC: \"PRIVATE_RANGES_ONLY\" or \"ALL_TRAFFIC\" (default: \"PRIVATE_RANGES_ONLY\")."
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
"name": "port",
|
|
1157
|
+
"type": "number",
|
|
1158
|
+
"required": false,
|
|
1159
|
+
"description": "Port the container listens on (default: 8080)."
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
"name": "frontendPort",
|
|
1163
|
+
"type": "number",
|
|
1164
|
+
"required": false,
|
|
1165
|
+
"description": "Port the HTTP forwarding rule listens on (default: 80)."
|
|
1166
|
+
},
|
|
1167
|
+
{
|
|
1168
|
+
"name": "minInstanceCount",
|
|
1169
|
+
"type": "number",
|
|
1170
|
+
"required": false,
|
|
1171
|
+
"description": "Minimum instances (default: 0)."
|
|
1172
|
+
},
|
|
1173
|
+
{
|
|
1174
|
+
"name": "maxInstanceCount",
|
|
1175
|
+
"type": "number",
|
|
1176
|
+
"required": false,
|
|
1177
|
+
"description": "Maximum instances (default: 100)."
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
"name": "env",
|
|
1181
|
+
"type": "Array<{ name: string; value: string }>",
|
|
1182
|
+
"required": false,
|
|
1183
|
+
"description": "Environment variables."
|
|
1184
|
+
},
|
|
1185
|
+
{
|
|
1186
|
+
"name": "serviceAccount",
|
|
1187
|
+
"type": "string",
|
|
1188
|
+
"required": false,
|
|
1189
|
+
"description": "An existing service account to run as: an email, or the account id of an IAMServiceAccount in the project."
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
"name": "domains",
|
|
1193
|
+
"type": "string[]",
|
|
1194
|
+
"required": false,
|
|
1195
|
+
"description": "Domains for a Google-managed certificate."
|
|
1196
|
+
},
|
|
1197
|
+
{
|
|
1198
|
+
"name": "securityPolicy",
|
|
1199
|
+
"type": "string",
|
|
1200
|
+
"required": false,
|
|
1201
|
+
"description": "Name of a ComputeSecurityPolicy (Cloud Armor) attached to the backend service."
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
"name": "labels",
|
|
1205
|
+
"type": "Record<string, string>",
|
|
1206
|
+
"required": false,
|
|
1207
|
+
"description": "Additional labels."
|
|
1208
|
+
},
|
|
1209
|
+
{
|
|
1210
|
+
"name": "namespace",
|
|
1211
|
+
"type": "string",
|
|
1212
|
+
"required": false,
|
|
1213
|
+
"description": "Namespace for all resources."
|
|
1214
|
+
},
|
|
1215
|
+
{
|
|
1216
|
+
"name": "defaults",
|
|
1217
|
+
"type": "{ runtime?: Partial<ConstructorParameters<typeof GCPServiceAccount>[0]>; service?: Record<string, unknown>; publicAcc...",
|
|
1218
|
+
"required": false,
|
|
1219
|
+
"description": "Per-member defaults for customizing individual resources."
|
|
1220
|
+
}
|
|
1221
|
+
]
|
|
1222
|
+
},
|
|
1036
1223
|
{
|
|
1037
1224
|
"name": "PubSubPipeline",
|
|
1038
1225
|
"lexicon": "gcp",
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { PrivateEgressNetwork } from "./private-egress-network";
|
|
3
|
+
import { PublicCloudRun } from "./public-cloud-run";
|
|
4
|
+
|
|
5
|
+
/** Helper to extract props from a Declarable member. */
|
|
6
|
+
function p(member: unknown): Record<string, any> {
|
|
7
|
+
return (member as any).props;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** The metadata.name a `{ name }` reference resolves to: a literal, or another member's name attribute. */
|
|
11
|
+
function refName(ref: { name: unknown }): unknown {
|
|
12
|
+
const value = ref.name as any;
|
|
13
|
+
return value?.parent?.deref ? p(value.parent.deref()).metadata.name : value;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const net = { vpc: { name: "web-vpc" }, subnet: { name: "web-apps" } };
|
|
17
|
+
const base = { name: "web", projectId: "proj", region: "us-central1", image: "img:1", ...net };
|
|
18
|
+
|
|
19
|
+
describe("PrivateEgressNetwork (#4263)", () => {
|
|
20
|
+
test("a custom-mode VPC and one subnet in the region", () => {
|
|
21
|
+
const { vpc, subnet } = PrivateEgressNetwork({ name: "web", region: "us-central1", subnetCidr: "10.10.0.0/24" });
|
|
22
|
+
expect(p(vpc).metadata.name).toBe("web-vpc");
|
|
23
|
+
expect(p(vpc).autoCreateSubnetworks).toBe(false);
|
|
24
|
+
expect(p(vpc).routingMode).toBe("REGIONAL");
|
|
25
|
+
expect(p(subnet).metadata.name).toBe("web-apps");
|
|
26
|
+
expect(p(subnet).region).toBe("us-central1");
|
|
27
|
+
expect(p(subnet).ipCidrRange).toBe("10.10.0.0/24");
|
|
28
|
+
expect(p(subnet).privateIpGoogleAccess).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("PublicCloudRun (#4263)", () => {
|
|
33
|
+
test("creates its own service account and runs the service as it", () => {
|
|
34
|
+
const r = PublicCloudRun(base);
|
|
35
|
+
expect(p(r.runtime).metadata.name).toBe("web-run");
|
|
36
|
+
expect(p(r.service).metadata.name).toBe("web-site");
|
|
37
|
+
expect(refName(p(r.service).template.serviceAccountRef)).toBe("web-run");
|
|
38
|
+
expect(p(r.service).projectRef).toEqual({ external: "proj" });
|
|
39
|
+
expect(p(r.publicAccess).member).toBe("allUsers");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("an existing serviceAccount creates no account", () => {
|
|
43
|
+
const r = PublicCloudRun({ ...base, serviceAccount: "x@o.iam.gserviceaccount.com" });
|
|
44
|
+
expect(r.runtime).toBeUndefined();
|
|
45
|
+
expect(p(r.service).template.serviceAccountRef).toEqual({ external: "x@o.iam.gserviceaccount.com" });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("Direct VPC egress into the subnet for private ranges", () => {
|
|
49
|
+
const r = PublicCloudRun(base);
|
|
50
|
+
expect(p(r.service).template.vpcAccess).toEqual({
|
|
51
|
+
egress: "PRIVATE_RANGES_ONLY",
|
|
52
|
+
networkInterfaces: [{ networkRef: { name: "web-vpc" }, subnetworkRef: { name: "web-apps" } }],
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("the load balancer chain: NEG, backend, URL map, HTTP proxy, address, rule", () => {
|
|
57
|
+
const r = PublicCloudRun(base);
|
|
58
|
+
expect(p(r.neg).networkEndpointType).toBe("SERVERLESS");
|
|
59
|
+
expect(refName(p(r.neg).cloudRun.serviceRef)).toBe("web-site");
|
|
60
|
+
expect(p(r.backend).loadBalancingScheme).toBe("EXTERNAL_MANAGED");
|
|
61
|
+
expect(p(r.backend).healthChecks).toBeUndefined();
|
|
62
|
+
expect(refName(p(r.backend).backend[0].group.networkEndpointGroupRef)).toBe("web-neg");
|
|
63
|
+
expect(refName(p(r.urlMap).defaultService.backendServiceRef)).toBe("web-backend");
|
|
64
|
+
expect(refName(p(r.proxy).urlMapRef)).toBe("web-map");
|
|
65
|
+
expect(p(r.address).location).toBe("global");
|
|
66
|
+
expect(p(r.rule).portRange).toBe("80");
|
|
67
|
+
expect(refName(p(r.rule).ipAddress.addressRef)).toBe("web-ip");
|
|
68
|
+
expect(refName(p(r.rule).target.targetHTTPProxyRef)).toBe("web-proxy");
|
|
69
|
+
expect(r.certificate).toBeUndefined();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("port sets the container, frontendPort the HTTP rule", () => {
|
|
73
|
+
const r = PublicCloudRun({ ...base, port: 3000, frontendPort: 8080 });
|
|
74
|
+
expect(p(r.service).template.containers[0].ports[0].containerPort).toBe(3000);
|
|
75
|
+
expect(p(r.rule).portRange).toBe("8080");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("domains add a managed certificate and HTTPS, and HTTP redirects", () => {
|
|
79
|
+
const r = PublicCloudRun({ ...base, domains: ["example.com"] });
|
|
80
|
+
expect(p(r.certificate).managed.domains).toEqual(["example.com"]);
|
|
81
|
+
expect(refName(p(r.httpsProxy).urlMapRef)).toBe("web-map");
|
|
82
|
+
expect(refName(p(r.httpsProxy).sslCertificates[0])).toBe("web-cert");
|
|
83
|
+
expect(p(r.httpsRule).portRange).toBe("443");
|
|
84
|
+
expect(refName(p(r.httpsRule).target.targetHTTPSProxyRef)).toBe("web-https-proxy");
|
|
85
|
+
expect(p(r.redirectMap).defaultUrlRedirect.httpsRedirect).toBe(true);
|
|
86
|
+
expect(refName(p(r.proxy).urlMapRef)).toBe("web-redirect");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("securityPolicy attaches to the backend service", () => {
|
|
90
|
+
const r = PublicCloudRun({ ...base, securityPolicy: "edge" });
|
|
91
|
+
expect(refName(p(r.backend).securityPolicyRef)).toBe("edge");
|
|
92
|
+
});
|
|
93
|
+
});
|