@intentius/chant-lexicon-gcp 0.124.35 → 0.124.36
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 +2 -1
- package/dist/api/read-client.d.ts.map +1 -1
- package/dist/api/read-client.js +2 -1
- package/dist/deep-observe.d.ts.map +1 -1
- package/dist/deep-observe.js +52 -0
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/dist/op/activities/gcp-apply.d.ts +7 -0
- package/dist/op/activities/gcp-apply.d.ts.map +1 -1
- package/dist/op/activities/gcp-apply.js +37 -2
- package/dist/op/activities/gcp-compute.d.ts +10 -0
- package/dist/op/activities/gcp-compute.d.ts.map +1 -1
- package/dist/op/activities/gcp-compute.js +39 -3
- package/dist/serializer.d.ts +6 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/serializer.js +21 -9
- package/package.json +2 -2
- package/src/api/read-client.ts +2 -1
- package/src/deep-observe.test.ts +27 -1
- package/src/deep-observe.ts +47 -0
- package/src/describe-resources.test.ts +1 -1
- package/src/op/activities/gcp-apply.test.ts +38 -0
- package/src/op/activities/gcp-apply.ts +36 -1
- package/src/op/activities/gcp-compute.test.ts +94 -0
- package/src/op/activities/gcp-compute.ts +37 -2
- package/src/serializer.test.ts +26 -0
- package/src/serializer.ts +22 -9
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
* PubSubTopic, PubSubSubscription, SecretManagerSecret, IAMServiceAccount,
|
|
25
25
|
* RunService, IAMPolicyMember and the Cloud Run load balancer kinds
|
|
26
26
|
* (ComputeAddress, ComputeRegionNetworkEndpointGroup, ComputeBackendService,
|
|
27
|
-
* ComputeURLMap, ComputeTargetHTTPProxy, ComputeForwardingRule; #4233)
|
|
27
|
+
* ComputeURLMap, ComputeTargetHTTPProxy, ComputeForwardingRule; #4233), and
|
|
28
|
+
* ComputeNetwork and ComputeSubnetwork (#4248). An
|
|
28
29
|
* IAMPolicyMember is read through its target's policy and is present when the
|
|
29
30
|
* policy grants its member the role. The kubectl path could fetch any CNRM kind the cluster knew
|
|
30
31
|
* about, so this is narrower on paper. It is not narrower in practice for
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read-client.d.ts","sourceRoot":"","sources":["../../src/api/read-client.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"read-client.d.ts","sourceRoot":"","sources":["../../src/api/read-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5F,kCAAkC;AAClC,MAAM,WAAW,oBAAoB;IACnC,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED,uHAAuH;AACvH,MAAM,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAUrH;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,KAAK;IAGnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM;IAF1B,YACE,OAAO,EAAE,MAAM,EACN,MAAM,CAAC,EAAE,MAAM,YAAA,EAIzB;CACF;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEhD;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEtE;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,oBAAoB,EAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAiDlC"}
|
package/dist/api/read-client.js
CHANGED
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
* PubSubTopic, PubSubSubscription, SecretManagerSecret, IAMServiceAccount,
|
|
25
25
|
* RunService, IAMPolicyMember and the Cloud Run load balancer kinds
|
|
26
26
|
* (ComputeAddress, ComputeRegionNetworkEndpointGroup, ComputeBackendService,
|
|
27
|
-
* ComputeURLMap, ComputeTargetHTTPProxy, ComputeForwardingRule; #4233)
|
|
27
|
+
* ComputeURLMap, ComputeTargetHTTPProxy, ComputeForwardingRule; #4233), and
|
|
28
|
+
* ComputeNetwork and ComputeSubnetwork (#4248). An
|
|
28
29
|
* IAMPolicyMember is read through its target's policy and is present when the
|
|
29
30
|
* policy grants its member the role. The kubectl path could fetch any CNRM kind the cluster knew
|
|
30
31
|
* about, so this is narrower on paper. It is not narrower in practice for
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deep-observe.d.ts","sourceRoot":"","sources":["../src/deep-observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EACV,qBAAqB,EAGtB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAajE,OAAO,EAAE,yBAAyB,EAAE,CAAC;AAErC,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWtF;AAUD;6EAC6E;AAC7E,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"deep-observe.d.ts","sourceRoot":"","sources":["../src/deep-observe.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EACV,qBAAqB,EAGtB,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAajE,OAAO,EAAE,yBAAyB,EAAE,CAAC;AAErC,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWtF;AAUD;6EAC6E;AAC7E,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA0OD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASnH;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA8F5G"}
|
package/dist/deep-observe.js
CHANGED
|
@@ -166,6 +166,25 @@ const CNRM_REFINERS = {
|
|
|
166
166
|
template.serviceAccountRef = email.endsWith(suffix) ? { name: email.slice(0, -suffix.length) } : { external: email };
|
|
167
167
|
delete template.serviceAccount;
|
|
168
168
|
}
|
|
169
|
+
// #4249: Direct VPC egress paths back to CNRM's refs, `name` for the
|
|
170
|
+
// project's own network and subnetwork, `external` otherwise.
|
|
171
|
+
const vpcAccess = template?.vpcAccess;
|
|
172
|
+
if (vpcAccess && Array.isArray(vpcAccess.networkInterfaces)) {
|
|
173
|
+
const own = (value, pattern) => {
|
|
174
|
+
if (!value.includes("/"))
|
|
175
|
+
return { name: value };
|
|
176
|
+
const hit = pattern.exec(value);
|
|
177
|
+
return hit && hit[1] === ctx.project ? { name: hit[2] } : { external: value };
|
|
178
|
+
};
|
|
179
|
+
vpcAccess.networkInterfaces = vpcAccess.networkInterfaces.map((i) => {
|
|
180
|
+
const { network, subnetwork, ...rest } = i;
|
|
181
|
+
return {
|
|
182
|
+
...rest,
|
|
183
|
+
...(typeof network === "string" ? { networkRef: own(network, /^projects\/([^/]+)\/global\/networks\/([^/]+)$/) } : {}),
|
|
184
|
+
...(typeof subnetwork === "string" ? { subnetworkRef: own(subnetwork, /^projects\/([^/]+)\/regions\/[^/]+\/subnetworks\/([^/]+)$/) } : {}),
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
}
|
|
169
188
|
},
|
|
170
189
|
// #4233: the inverse of gcp-compute.ts's body builders. Compute returns refs
|
|
171
190
|
// as full URLs and a region as a URL; CNRM writes `*Ref: { name }` and a
|
|
@@ -247,6 +266,39 @@ const CNRM_REFINERS = {
|
|
|
247
266
|
delete tree.urlMap;
|
|
248
267
|
}
|
|
249
268
|
},
|
|
269
|
+
// #4248: a network is global and has no location; compute nests the routing
|
|
270
|
+
// mode and lists the subnetworks it holds, which CNRM declares separately.
|
|
271
|
+
ComputeNetwork(tree) {
|
|
272
|
+
const routing = tree.routingConfig;
|
|
273
|
+
if (routing) {
|
|
274
|
+
if (routing.routingMode !== undefined)
|
|
275
|
+
tree.routingMode = routing.routingMode;
|
|
276
|
+
delete tree.routingConfig;
|
|
277
|
+
}
|
|
278
|
+
delete tree.subnetworks;
|
|
279
|
+
delete tree.gatewayIPv4;
|
|
280
|
+
},
|
|
281
|
+
ComputeSubnetwork(tree) {
|
|
282
|
+
if (typeof tree.region === "string")
|
|
283
|
+
tree.region = lastSegment(tree.region);
|
|
284
|
+
if (typeof tree.network === "string") {
|
|
285
|
+
tree.networkRef = { name: lastSegment(tree.network) };
|
|
286
|
+
delete tree.network;
|
|
287
|
+
}
|
|
288
|
+
if (Array.isArray(tree.secondaryIpRanges)) {
|
|
289
|
+
tree.secondaryIpRange = tree.secondaryIpRanges;
|
|
290
|
+
delete tree.secondaryIpRanges;
|
|
291
|
+
}
|
|
292
|
+
const log = tree.logConfig;
|
|
293
|
+
if (log) {
|
|
294
|
+
if (log.enable === false)
|
|
295
|
+
delete tree.logConfig;
|
|
296
|
+
else
|
|
297
|
+
delete log.enable;
|
|
298
|
+
}
|
|
299
|
+
delete tree.gatewayAddress;
|
|
300
|
+
delete tree.fingerprint;
|
|
301
|
+
},
|
|
250
302
|
ComputeForwardingRule(tree) {
|
|
251
303
|
computeLocation(tree);
|
|
252
304
|
if (tree.IPProtocol !== undefined) {
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "9c67ab15a45ae8ce5c5ac3a6e3a83295bd28e78a4acf71b0d00d74040f5d27e0",
|
|
5
5
|
"meta.json": "75643660010c044c6f465bef2688a864ab30cc96fd9b68111969749748ac64ca",
|
|
6
6
|
"types/index.d.ts": "da1ebc6bcf83e1a018a773b59714194aeeb74ae1d5c1252b737b7e730ca98bd5",
|
|
7
7
|
"rules/hardcoded-project.ts": "6d5ae9efa74d73800eebe1a0a0d43c619ee0fdebe3347195df93fcbe63b34f0d",
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"skills/chant-gcp-patterns.md": "4c6809fc7c37fa1879379ad4c0e2d114dec9497bc6eb9d1bca4e79fddea4f509",
|
|
41
41
|
"skills/chant-gcp-gke.md": "be277019da9a722c851e47cd2dfb9c9536668948c3535fb20db7697e934c4e2b"
|
|
42
42
|
},
|
|
43
|
-
"composite": "
|
|
43
|
+
"composite": "f841125ccfbe364d6981cf4a1f444c9edeaa213e3b5d562dce7b12b341d82daf"
|
|
44
44
|
}
|
package/dist/manifest.json
CHANGED
|
@@ -189,6 +189,13 @@ export declare const pubSubTopicMapper: ResourceMapper;
|
|
|
189
189
|
export declare function cloudRunServiceBody(resource: GcpResource, project?: string): {
|
|
190
190
|
template?: unknown;
|
|
191
191
|
};
|
|
192
|
+
/**
|
|
193
|
+
* A CNRM Direct VPC egress interface as Cloud Run v2 takes it (#4249):
|
|
194
|
+
* `networkRef` and `subnetworkRef` become `network` and `subnetwork` paths,
|
|
195
|
+
* `name` in the project (the subnetwork in the service's region) and
|
|
196
|
+
* `external` as given. `tags` and anything else pass through. Pure.
|
|
197
|
+
*/
|
|
198
|
+
export declare function networkInterface(iface: unknown, project: string | undefined, region: string): unknown;
|
|
192
199
|
/**
|
|
193
200
|
* The email a CNRM `serviceAccountRef` names: `external` as given (an email,
|
|
194
201
|
* or a `projects/<p>/serviceAccounts/<email>` path), `name` as an account id
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcp-apply.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-apply.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gcp-apply.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { sleep } from "@intentius/chant/op";
|
|
|
4
4
|
import { hasOwnershipMarker, OWNERSHIP_MANAGED_BY_VALUE } from "@intentius/chant/ownership";
|
|
5
5
|
import { GCP_RESOURCE_OWNERSHIP_KEYS } from "../../ownership.js";
|
|
6
6
|
import { iamPolicyMemberMapper } from "./gcp-iam.js";
|
|
7
|
-
import { backendServiceMapper, computeAddressMapper, forwardingRuleMapper, regionNetworkEndpointGroupMapper, targetHttpProxyMapper, urlMapMapper, } from "./gcp-compute.js";
|
|
7
|
+
import { backendServiceMapper, computeAddressMapper, forwardingRuleMapper, networkMapper, subnetworkMapper, regionNetworkEndpointGroupMapper, targetHttpProxyMapper, urlMapMapper, } from "./gcp-compute.js";
|
|
8
8
|
import { applyResult, } from "@intentius/chant/apply";
|
|
9
9
|
const PROJECT_ID_ANNOTATION = "cnrm.cloud.google.com/project-id";
|
|
10
10
|
/**
|
|
@@ -157,9 +157,40 @@ export function cloudRunServiceBody(resource, project) {
|
|
|
157
157
|
return body;
|
|
158
158
|
const { serviceAccountRef, ...template } = spec.template;
|
|
159
159
|
const email = serviceAccountEmail(serviceAccountRef, project);
|
|
160
|
-
|
|
160
|
+
const out = email ? { ...template, serviceAccount: email } : { ...template };
|
|
161
|
+
const vpcAccess = out.vpcAccess;
|
|
162
|
+
if (vpcAccess && Array.isArray(vpcAccess.networkInterfaces)) {
|
|
163
|
+
const region = (resource.spec ?? {}).location ?? "us-central1";
|
|
164
|
+
out.vpcAccess = { ...vpcAccess, networkInterfaces: vpcAccess.networkInterfaces.map((i) => networkInterface(i, project, region)) };
|
|
165
|
+
}
|
|
166
|
+
body.template = out;
|
|
161
167
|
return body;
|
|
162
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* A CNRM Direct VPC egress interface as Cloud Run v2 takes it (#4249):
|
|
171
|
+
* `networkRef` and `subnetworkRef` become `network` and `subnetwork` paths,
|
|
172
|
+
* `name` in the project (the subnetwork in the service's region) and
|
|
173
|
+
* `external` as given. `tags` and anything else pass through. Pure.
|
|
174
|
+
*/
|
|
175
|
+
export function networkInterface(iface, project, region) {
|
|
176
|
+
if (!iface || typeof iface !== "object")
|
|
177
|
+
return iface;
|
|
178
|
+
const { networkRef, subnetworkRef, ...rest } = iface;
|
|
179
|
+
const path = (ref, scoped, key) => {
|
|
180
|
+
if (!ref)
|
|
181
|
+
return undefined;
|
|
182
|
+
if (ref.external)
|
|
183
|
+
return ref.external;
|
|
184
|
+
if (!ref.name)
|
|
185
|
+
return undefined;
|
|
186
|
+
if (!project)
|
|
187
|
+
throw new Error(`${key} { name: "${ref.name}" } needs a project to form its path`);
|
|
188
|
+
return scoped(project, ref.name);
|
|
189
|
+
};
|
|
190
|
+
const network = path(networkRef, (p, n) => `projects/${p}/global/networks/${n}`, "networkRef");
|
|
191
|
+
const subnetwork = path(subnetworkRef, (p, n) => `projects/${p}/regions/${region}/subnetworks/${n}`, "subnetworkRef");
|
|
192
|
+
return { ...rest, ...(network ? { network } : {}), ...(subnetwork ? { subnetwork } : {}) };
|
|
193
|
+
}
|
|
163
194
|
/**
|
|
164
195
|
* The email a CNRM `serviceAccountRef` names: `external` as given (an email,
|
|
165
196
|
* or a `projects/<p>/serviceAccounts/<email>` path), `name` as an account id
|
|
@@ -295,6 +326,8 @@ export const MAPPERS = {
|
|
|
295
326
|
ComputeURLMap: urlMapMapper,
|
|
296
327
|
ComputeTargetHTTPProxy: targetHttpProxyMapper,
|
|
297
328
|
ComputeForwardingRule: forwardingRuleMapper,
|
|
329
|
+
ComputeNetwork: networkMapper,
|
|
330
|
+
ComputeSubnetwork: subnetworkMapper,
|
|
298
331
|
};
|
|
299
332
|
/**
|
|
300
333
|
* Collect the local resource names this resource references via CNRM `*Ref`
|
|
@@ -319,6 +352,8 @@ const REF_KINDS = {
|
|
|
319
352
|
backendServiceRef: "ComputeBackendService",
|
|
320
353
|
urlMapRef: "ComputeURLMap",
|
|
321
354
|
targetHTTPProxyRef: "ComputeTargetHTTPProxy",
|
|
355
|
+
networkRef: "ComputeNetwork",
|
|
356
|
+
subnetworkRef: "ComputeSubnetwork",
|
|
322
357
|
};
|
|
323
358
|
/** The local resources this resource references via `*Ref` fields, each with its kind when known. Pure. */
|
|
324
359
|
export function referencedResources(resource) {
|
|
@@ -31,6 +31,14 @@ export declare function computeLabels(labels: Record<string, string> | undefined
|
|
|
31
31
|
export declare function addressBody(resource: GcpResource, ctx: ComputeCtx): Obj;
|
|
32
32
|
/** ComputeRegionNetworkEndpointGroup → regionNetworkEndpointGroups.insert body (serverless NEGs). Pure. */
|
|
33
33
|
export declare function negBody(resource: GcpResource, ctx: ComputeCtx): Obj;
|
|
34
|
+
/**
|
|
35
|
+
* ComputeNetwork → networks.insert body. A network is global. CNRM's
|
|
36
|
+
* `autoCreateSubnetworks` defaults to false, where compute's default is true,
|
|
37
|
+
* so it is always sent. Pure.
|
|
38
|
+
*/
|
|
39
|
+
export declare function networkBody(resource: GcpResource): Obj;
|
|
40
|
+
/** ComputeSubnetwork → subnetworks.insert body. CNRM's `secondaryIpRange` and `logConfig` become compute's `secondaryIpRanges` and an enabled `logConfig`. Pure. */
|
|
41
|
+
export declare function subnetworkBody(resource: GcpResource, ctx: ComputeCtx): Obj;
|
|
34
42
|
/** ComputeBackendService → backendServices.insert body. Pure. */
|
|
35
43
|
export declare function backendServiceBody(resource: GcpResource, ctx: ComputeCtx): Obj;
|
|
36
44
|
/** ComputeURLMap → urlMaps.insert body. Pure. */
|
|
@@ -52,5 +60,7 @@ export declare const backendServiceMapper: ResourceMapper;
|
|
|
52
60
|
export declare const urlMapMapper: ResourceMapper;
|
|
53
61
|
export declare const targetHttpProxyMapper: ResourceMapper;
|
|
54
62
|
export declare const forwardingRuleMapper: ResourceMapper;
|
|
63
|
+
export declare const networkMapper: ResourceMapper;
|
|
64
|
+
export declare const subnetworkMapper: ResourceMapper;
|
|
55
65
|
export {};
|
|
56
66
|
//# sourceMappingURL=gcp-compute.d.ts.map
|
|
@@ -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":"AAsBA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9E,eAAO,MAAM,YAAY,mCAAmC,CAAC;AAI7D,KAAK,GAAG,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAC/D,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAqBnC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;CACnC;AAED,wGAAwG;AACxG,wBAAgB,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,SAAS,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAG3F;AAMD,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,CAKzD;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAmB5F;AAkBD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMhG;AAED,oDAAoD;AACpD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,GAAG,GAAG,CAWvE;AAED,2GAA2G;AAC3G,wBAAgB,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,GAAG,GAAG,CA0BnE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,WAAW,GAAG,GAAG,CAStD;AAED,oKAAoK;AACpK,wBAAgB,cAAc,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,GAAG,GAAG,CAW1E;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,GAAG,GAAG,CAoC9E;AAED,iDAAiD;AACjD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,GAAG,GAAG,CA4BtE;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,GAAG,GAAG,CAK/E;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,GAAG,GAAG,CAoB9E;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,aAe9B,CAAC;AAsBF,eAAO,MAAM,oBAAoB,gBAA4D,CAAC;AAC9F,eAAO,MAAM,gCAAgC,gBAAuF,CAAC;AACrI,eAAO,MAAM,oBAAoB,gBAAgF,CAAC;AAClH,eAAO,MAAM,YAAY,gBAAwD,CAAC;AAClF,eAAO,MAAM,qBAAqB,gBAAoF,CAAC;AACvH,eAAO,MAAM,oBAAoB,gBAAgF,CAAC;AAClH,eAAO,MAAM,aAAa,gBAAuF,CAAC;AAClH,eAAO,MAAM,gBAAgB,gBAAoE,CAAC"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compute Engine mappers for the Cloud Run load balancer (#4233): ComputeAddress,
|
|
3
3
|
* ComputeRegionNetworkEndpointGroup (serverless NEGs), ComputeBackendService,
|
|
4
|
-
* ComputeURLMap, ComputeTargetHTTPProxy and ComputeForwardingRule
|
|
4
|
+
* ComputeURLMap, ComputeTargetHTTPProxy and ComputeForwardingRule; and for a
|
|
5
|
+
* declared network (#4248): ComputeNetwork (always global) and
|
|
6
|
+
* ComputeSubnetwork (regional, by its CNRM `region`).
|
|
5
7
|
*
|
|
6
8
|
* Each maps a CNRM spec onto the compute v1 insert body. A resource is global
|
|
7
9
|
* or regional by its CNRM `location` (`"global"` or a region; a NEG has
|
|
@@ -158,6 +160,38 @@ export function negBody(resource, ctx) {
|
|
|
158
160
|
body.subnetwork = refUrl("subnetworkRef", spec.subnetworkRef, ctx, scope);
|
|
159
161
|
return body;
|
|
160
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* ComputeNetwork → networks.insert body. A network is global. CNRM's
|
|
165
|
+
* `autoCreateSubnetworks` defaults to false, where compute's default is true,
|
|
166
|
+
* so it is always sent. Pure.
|
|
167
|
+
*/
|
|
168
|
+
export function networkBody(resource) {
|
|
169
|
+
const spec = (resource.spec ?? {});
|
|
170
|
+
const body = {
|
|
171
|
+
name: computeName(resource),
|
|
172
|
+
autoCreateSubnetworks: spec.autoCreateSubnetworks === true,
|
|
173
|
+
...pick(spec, ["description", "mtu", "networkFirewallPolicyEnforcementOrder"]),
|
|
174
|
+
};
|
|
175
|
+
if (typeof spec.routingMode === "string")
|
|
176
|
+
body.routingConfig = { routingMode: spec.routingMode };
|
|
177
|
+
return body;
|
|
178
|
+
}
|
|
179
|
+
/** ComputeSubnetwork → subnetworks.insert body. CNRM's `secondaryIpRange` and `logConfig` become compute's `secondaryIpRanges` and an enabled `logConfig`. Pure. */
|
|
180
|
+
export function subnetworkBody(resource, ctx) {
|
|
181
|
+
const spec = (resource.spec ?? {});
|
|
182
|
+
const scope = scopeOf(spec);
|
|
183
|
+
const body = {
|
|
184
|
+
name: computeName(resource),
|
|
185
|
+
...pick(spec, ["ipCidrRange", "description", "privateIpGoogleAccess", "purpose", "role", "stackType"]),
|
|
186
|
+
};
|
|
187
|
+
if (isObj(spec.networkRef))
|
|
188
|
+
body.network = refUrl("networkRef", spec.networkRef, ctx, scope);
|
|
189
|
+
if (Array.isArray(spec.secondaryIpRange))
|
|
190
|
+
body.secondaryIpRanges = spec.secondaryIpRange;
|
|
191
|
+
if (isObj(spec.logConfig))
|
|
192
|
+
body.logConfig = { enable: true, ...spec.logConfig };
|
|
193
|
+
return body;
|
|
194
|
+
}
|
|
161
195
|
/** ComputeBackendService → backendServices.insert body. Pure. */
|
|
162
196
|
export function backendServiceBody(resource, ctx) {
|
|
163
197
|
const spec = (resource.spec ?? {});
|
|
@@ -292,13 +326,13 @@ export const computeOperation = {
|
|
|
292
326
|
return errors?.length ? errors.map((e) => e.message ?? e.code ?? "error").join("; ") : undefined;
|
|
293
327
|
},
|
|
294
328
|
};
|
|
295
|
-
function computeMapper(kind, collection, build, regionalCollection = collection) {
|
|
329
|
+
function computeMapper(kind, collection, build, regionalCollection = collection, scopeFor = (spec) => scopeOf(spec)) {
|
|
296
330
|
return {
|
|
297
331
|
kind,
|
|
298
332
|
defaultHost: COMPUTE_HOST,
|
|
299
333
|
operation: computeOperation,
|
|
300
334
|
plan(resource, ctx) {
|
|
301
|
-
const scope =
|
|
335
|
+
const scope = scopeFor(resource.spec);
|
|
302
336
|
const coll = `${ctx.base}/compute/v1/${scopePath(ctx.project, scope)}/${scope === "global" ? collection : regionalCollection}`;
|
|
303
337
|
const name = computeName(resource);
|
|
304
338
|
return { getUrl: `${coll}/${enc(name)}`, create: { method: "POST", url: coll, body: build(resource, ctx) } };
|
|
@@ -311,3 +345,5 @@ export const backendServiceMapper = computeMapper("ComputeBackendService", "back
|
|
|
311
345
|
export const urlMapMapper = computeMapper("ComputeURLMap", "urlMaps", urlMapBody);
|
|
312
346
|
export const targetHttpProxyMapper = computeMapper("ComputeTargetHTTPProxy", "targetHttpProxies", targetHttpProxyBody);
|
|
313
347
|
export const forwardingRuleMapper = computeMapper("ComputeForwardingRule", "forwardingRules", forwardingRuleBody);
|
|
348
|
+
export const networkMapper = computeMapper("ComputeNetwork", "networks", networkBody, "networks", () => "global");
|
|
349
|
+
export const subnetworkMapper = computeMapper("ComputeSubnetwork", "subnetworks", subnetworkBody);
|
package/dist/serializer.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Converts Chant declarables to multi-document K8s YAML output
|
|
5
5
|
* for Config Connector resources with apiVersion, kind, metadata, and spec.
|
|
6
6
|
*/
|
|
7
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
7
8
|
import type { Serializer } from "@intentius/chant/serializer";
|
|
8
9
|
/**
|
|
9
10
|
* Resolve entityType to apiVersion and kind.
|
|
@@ -17,6 +18,11 @@ export declare function resolveGVK(entityType: string): {
|
|
|
17
18
|
apiVersion: string;
|
|
18
19
|
kind: string;
|
|
19
20
|
} | null;
|
|
21
|
+
/**
|
|
22
|
+
* The metadata.name a resource is written with: a string `metadata.name` in its
|
|
23
|
+
* props, else its logical id in kebab case.
|
|
24
|
+
*/
|
|
25
|
+
export declare function cnrmName(logical: string, entity: Declarable): string;
|
|
20
26
|
/**
|
|
21
27
|
* GCP Config Connector YAML serializer implementation.
|
|
22
28
|
*/
|
package/dist/serializer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AAiClG;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAW1F;AA+CD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAIpE;AA4BD;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAiG3B,CAAC"}
|
package/dist/serializer.js
CHANGED
|
@@ -62,15 +62,15 @@ function deriveGVKFromType(entityType) {
|
|
|
62
62
|
* GCP visitor for the generic serializer walker.
|
|
63
63
|
*/
|
|
64
64
|
function gcpVisitor(entityNames) {
|
|
65
|
+
// Config Connector resolves a `*Ref: { name }` by the target's metadata.name,
|
|
66
|
+
// which is its declared name or else the kebab-cased logical id (#4242).
|
|
67
|
+
const cnrmNames = new Map();
|
|
68
|
+
for (const [entity, logical] of entityNames)
|
|
69
|
+
cnrmNames.set(logical, cnrmName(logical, entity));
|
|
70
|
+
const resolve = (logical) => cnrmNames.get(logical) ?? logical;
|
|
65
71
|
return {
|
|
66
|
-
attrRef: (name, _attr) =>
|
|
67
|
-
|
|
68
|
-
return name;
|
|
69
|
-
},
|
|
70
|
-
resourceRef: (name) => {
|
|
71
|
-
// Config Connector uses resourceRef pattern: { name: "resource-name" }
|
|
72
|
-
return { name };
|
|
73
|
-
},
|
|
72
|
+
attrRef: (name, _attr) => resolve(name),
|
|
73
|
+
resourceRef: (name) => ({ name: resolve(name) }),
|
|
74
74
|
propertyDeclarable: (entity, walk) => {
|
|
75
75
|
if (!isResourceDeclarable(entity) || typeof entity.props !== "object" || entity.props === null) {
|
|
76
76
|
return undefined;
|
|
@@ -86,6 +86,18 @@ function gcpVisitor(entityNames) {
|
|
|
86
86
|
},
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* The metadata.name a resource is written with: a string `metadata.name` in its
|
|
91
|
+
* props, else its logical id in kebab case.
|
|
92
|
+
*/
|
|
93
|
+
export function cnrmName(logical, entity) {
|
|
94
|
+
const props = isResourceDeclarable(entity) ? entity.props : undefined;
|
|
95
|
+
const declared = props?.metadata?.name;
|
|
96
|
+
return typeof declared === "string" && declared.length > 0 ? declared : kebabName(logical);
|
|
97
|
+
}
|
|
98
|
+
function kebabName(logical) {
|
|
99
|
+
return logical.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
100
|
+
}
|
|
89
101
|
/**
|
|
90
102
|
* Convert a value to YAML-compatible form using the walker.
|
|
91
103
|
*/
|
|
@@ -152,7 +164,7 @@ export const gcpSerializer = {
|
|
|
152
164
|
// Build metadata
|
|
153
165
|
const metadata = props.metadata ?? {};
|
|
154
166
|
if (!metadata.name) {
|
|
155
|
-
metadata.name = name
|
|
167
|
+
metadata.name = kebabName(name);
|
|
156
168
|
}
|
|
157
169
|
// Merge default labels
|
|
158
170
|
if (Object.keys(defaultLabelEntries).length > 0) {
|
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.36",
|
|
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.36"
|
|
80
80
|
}
|
|
81
81
|
}
|
package/src/api/read-client.ts
CHANGED
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
* PubSubTopic, PubSubSubscription, SecretManagerSecret, IAMServiceAccount,
|
|
25
25
|
* RunService, IAMPolicyMember and the Cloud Run load balancer kinds
|
|
26
26
|
* (ComputeAddress, ComputeRegionNetworkEndpointGroup, ComputeBackendService,
|
|
27
|
-
* ComputeURLMap, ComputeTargetHTTPProxy, ComputeForwardingRule; #4233)
|
|
27
|
+
* ComputeURLMap, ComputeTargetHTTPProxy, ComputeForwardingRule; #4233), and
|
|
28
|
+
* ComputeNetwork and ComputeSubnetwork (#4248). An
|
|
28
29
|
* IAMPolicyMember is read through its target's policy and is present when the
|
|
29
30
|
* policy grants its member the role. The kubectl path could fetch any CNRM kind the cluster knew
|
|
30
31
|
* about, so this is narrower on paper. It is not narrower in practice for
|
package/src/deep-observe.test.ts
CHANGED
|
@@ -139,6 +139,32 @@ describe("toCnrmTree — each REST shape in the declared vocabulary (#1210)", ()
|
|
|
139
139
|
expect(other.template).toEqual({ serviceAccountRef: { external: "x@other.iam.gserviceaccount.com" } });
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
+
test("run service: vpcAccess network and subnetwork paths map back to refs (#4249)", () => {
|
|
143
|
+
const tree = toCnrmTree(
|
|
144
|
+
"RunService",
|
|
145
|
+
{
|
|
146
|
+
name: "projects/my-project/locations/europe-west1/services/web",
|
|
147
|
+
template: {
|
|
148
|
+
vpcAccess: {
|
|
149
|
+
egress: "PRIVATE_RANGES_ONLY",
|
|
150
|
+
networkInterfaces: [
|
|
151
|
+
{ network: "projects/my-project/global/networks/vpc", subnetwork: "projects/my-project/regions/europe-west1/subnetworks/apps" },
|
|
152
|
+
{ network: "projects/other/global/networks/shared", subnetwork: "apps" },
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
ctx,
|
|
158
|
+
) as { template: { vpcAccess: unknown } };
|
|
159
|
+
expect(tree.template.vpcAccess).toEqual({
|
|
160
|
+
egress: "PRIVATE_RANGES_ONLY",
|
|
161
|
+
networkInterfaces: [
|
|
162
|
+
{ networkRef: { name: "vpc" }, subnetworkRef: { name: "apps" } },
|
|
163
|
+
{ networkRef: { external: "projects/other/global/networks/shared" }, subnetworkRef: { name: "apps" } },
|
|
164
|
+
],
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
142
168
|
test("bucket: GCS's iamConfiguration and lifecycle map onto the CNRM field names", () => {
|
|
143
169
|
const tree = toCnrmTree(
|
|
144
170
|
"StorageBucket",
|
|
@@ -250,7 +276,7 @@ describe("observeResourcesDeepGcp — over REST (#1209)", () => {
|
|
|
250
276
|
});
|
|
251
277
|
|
|
252
278
|
test("a kind the applier cannot write is unsupported-kind, and is never queried", async () => {
|
|
253
|
-
const out = await observe(entities([["e", "GCP::Compute::
|
|
279
|
+
const out = await observe(entities([["e", "GCP::Compute::Firewall", { metadata: { name: "n" } }]]));
|
|
254
280
|
expect(out.unobserved?.e?.reason).toBe("unsupported-kind");
|
|
255
281
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
256
282
|
});
|
package/src/deep-observe.ts
CHANGED
|
@@ -191,6 +191,24 @@ const CNRM_REFINERS: Record<string, (tree: CnrmTree, ctx: RefineContext) => void
|
|
|
191
191
|
template.serviceAccountRef = email.endsWith(suffix) ? { name: email.slice(0, -suffix.length) } : { external: email };
|
|
192
192
|
delete template.serviceAccount;
|
|
193
193
|
}
|
|
194
|
+
// #4249: Direct VPC egress paths back to CNRM's refs, `name` for the
|
|
195
|
+
// project's own network and subnetwork, `external` otherwise.
|
|
196
|
+
const vpcAccess = template?.vpcAccess as { networkInterfaces?: unknown } | undefined;
|
|
197
|
+
if (vpcAccess && Array.isArray(vpcAccess.networkInterfaces)) {
|
|
198
|
+
const own = (value: string, pattern: RegExp): { name: string } | { external: string } => {
|
|
199
|
+
if (!value.includes("/")) return { name: value };
|
|
200
|
+
const hit = pattern.exec(value);
|
|
201
|
+
return hit && hit[1] === ctx.project ? { name: hit[2] } : { external: value };
|
|
202
|
+
};
|
|
203
|
+
vpcAccess.networkInterfaces = (vpcAccess.networkInterfaces as Array<Record<string, unknown>>).map((i) => {
|
|
204
|
+
const { network, subnetwork, ...rest } = i;
|
|
205
|
+
return {
|
|
206
|
+
...rest,
|
|
207
|
+
...(typeof network === "string" ? { networkRef: own(network, /^projects\/([^/]+)\/global\/networks\/([^/]+)$/) } : {}),
|
|
208
|
+
...(typeof subnetwork === "string" ? { subnetworkRef: own(subnetwork, /^projects\/([^/]+)\/regions\/[^/]+\/subnetworks\/([^/]+)$/) } : {}),
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
}
|
|
194
212
|
},
|
|
195
213
|
// #4233: the inverse of gcp-compute.ts's body builders. Compute returns refs
|
|
196
214
|
// as full URLs and a region as a URL; CNRM writes `*Ref: { name }` and a
|
|
@@ -269,6 +287,35 @@ const CNRM_REFINERS: Record<string, (tree: CnrmTree, ctx: RefineContext) => void
|
|
|
269
287
|
delete tree.urlMap;
|
|
270
288
|
}
|
|
271
289
|
},
|
|
290
|
+
// #4248: a network is global and has no location; compute nests the routing
|
|
291
|
+
// mode and lists the subnetworks it holds, which CNRM declares separately.
|
|
292
|
+
ComputeNetwork(tree) {
|
|
293
|
+
const routing = tree.routingConfig as { routingMode?: unknown } | undefined;
|
|
294
|
+
if (routing) {
|
|
295
|
+
if (routing.routingMode !== undefined) tree.routingMode = routing.routingMode;
|
|
296
|
+
delete tree.routingConfig;
|
|
297
|
+
}
|
|
298
|
+
delete tree.subnetworks;
|
|
299
|
+
delete tree.gatewayIPv4;
|
|
300
|
+
},
|
|
301
|
+
ComputeSubnetwork(tree) {
|
|
302
|
+
if (typeof tree.region === "string") tree.region = lastSegment(tree.region);
|
|
303
|
+
if (typeof tree.network === "string") {
|
|
304
|
+
tree.networkRef = { name: lastSegment(tree.network) };
|
|
305
|
+
delete tree.network;
|
|
306
|
+
}
|
|
307
|
+
if (Array.isArray(tree.secondaryIpRanges)) {
|
|
308
|
+
tree.secondaryIpRange = tree.secondaryIpRanges;
|
|
309
|
+
delete tree.secondaryIpRanges;
|
|
310
|
+
}
|
|
311
|
+
const log = tree.logConfig as { enable?: unknown } | undefined;
|
|
312
|
+
if (log) {
|
|
313
|
+
if (log.enable === false) delete tree.logConfig;
|
|
314
|
+
else delete log.enable;
|
|
315
|
+
}
|
|
316
|
+
delete tree.gatewayAddress;
|
|
317
|
+
delete tree.fingerprint;
|
|
318
|
+
},
|
|
272
319
|
ComputeForwardingRule(tree) {
|
|
273
320
|
computeLocation(tree);
|
|
274
321
|
if (tree.IPProtocol !== undefined) {
|
|
@@ -80,7 +80,7 @@ describe("gcp describeResources — direct REST (#1209)", () => {
|
|
|
80
80
|
|
|
81
81
|
test("a kind the applier has no mapper for is unsupported-kind, not absent", async () => {
|
|
82
82
|
const out = await read(
|
|
83
|
-
makeEntities([{ name: "e", entityType: "GCP::Compute::
|
|
83
|
+
makeEntities([{ name: "e", entityType: "GCP::Compute::Firewall", props: { metadata: { name: "net" } } }]),
|
|
84
84
|
);
|
|
85
85
|
expect(out.unobserved?.e?.reason).toBe("unsupported-kind");
|
|
86
86
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
bucketInsertBody,
|
|
6
6
|
pubSubTopicBody,
|
|
7
7
|
cloudRunServiceBody,
|
|
8
|
+
networkInterface,
|
|
8
9
|
serviceAccountEmail,
|
|
9
10
|
resolveGcpProject,
|
|
10
11
|
resolveGcpEndpoint,
|
|
@@ -122,7 +123,9 @@ describe("mappers build correct plans (#706)", () => {
|
|
|
122
123
|
"ComputeAddress",
|
|
123
124
|
"ComputeBackendService",
|
|
124
125
|
"ComputeForwardingRule",
|
|
126
|
+
"ComputeNetwork",
|
|
125
127
|
"ComputeRegionNetworkEndpointGroup",
|
|
128
|
+
"ComputeSubnetwork",
|
|
126
129
|
"ComputeTargetHTTPProxy",
|
|
127
130
|
"ComputeURLMap",
|
|
128
131
|
"IAMPolicyMember",
|
|
@@ -164,6 +167,41 @@ describe("Cloud Run mapper + LRO (#706)", () => {
|
|
|
164
167
|
expect(() => serviceAccountEmail({ name: "run-sa" })).toThrow(/needs a project/);
|
|
165
168
|
});
|
|
166
169
|
|
|
170
|
+
test("cloudRunServiceBody sends vpcAccess refs as network and subnetwork paths (#4249)", () => {
|
|
171
|
+
const resource = {
|
|
172
|
+
...RUN_SERVICE,
|
|
173
|
+
spec: {
|
|
174
|
+
...(RUN_SERVICE.spec as object),
|
|
175
|
+
location: "europe-west1",
|
|
176
|
+
template: {
|
|
177
|
+
containers: [{ image: "i" }],
|
|
178
|
+
vpcAccess: {
|
|
179
|
+
egress: "PRIVATE_RANGES_ONLY",
|
|
180
|
+
networkInterfaces: [{ networkRef: { name: "vpc" }, subnetworkRef: { name: "apps" }, tags: ["run"] }],
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
expect(cloudRunServiceBody(resource, "p")).toEqual({
|
|
186
|
+
template: {
|
|
187
|
+
containers: [{ image: "i" }],
|
|
188
|
+
vpcAccess: {
|
|
189
|
+
egress: "PRIVATE_RANGES_ONLY",
|
|
190
|
+
networkInterfaces: [
|
|
191
|
+
{ tags: ["run"], network: "projects/p/global/networks/vpc", subnetwork: "projects/p/regions/europe-west1/subnetworks/apps" },
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("networkInterface: external as given, name needs a project", () => {
|
|
199
|
+
expect(networkInterface({ networkRef: { external: "projects/o/global/networks/shared" } }, "p", "r")).toEqual({
|
|
200
|
+
network: "projects/o/global/networks/shared",
|
|
201
|
+
});
|
|
202
|
+
expect(() => networkInterface({ subnetworkRef: { name: "apps" } }, undefined, "r")).toThrow(/needs a project/);
|
|
203
|
+
});
|
|
204
|
+
|
|
167
205
|
test("RunService plan sends the service account email on create and update", () => {
|
|
168
206
|
const resource = {
|
|
169
207
|
...RUN_SERVICE,
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
backendServiceMapper,
|
|
9
9
|
computeAddressMapper,
|
|
10
10
|
forwardingRuleMapper,
|
|
11
|
+
networkMapper,
|
|
12
|
+
subnetworkMapper,
|
|
11
13
|
regionNetworkEndpointGroupMapper,
|
|
12
14
|
targetHttpProxyMapper,
|
|
13
15
|
urlMapMapper,
|
|
@@ -292,10 +294,39 @@ export function cloudRunServiceBody(resource: GcpResource, project?: string): {
|
|
|
292
294
|
if (!spec.template) return body;
|
|
293
295
|
const { serviceAccountRef, ...template } = spec.template as { serviceAccountRef?: { name?: string; external?: string } };
|
|
294
296
|
const email = serviceAccountEmail(serviceAccountRef, project);
|
|
295
|
-
|
|
297
|
+
const out: Record<string, unknown> = email ? { ...template, serviceAccount: email } : { ...template };
|
|
298
|
+
const vpcAccess = out.vpcAccess as { networkInterfaces?: unknown } | undefined;
|
|
299
|
+
if (vpcAccess && Array.isArray(vpcAccess.networkInterfaces)) {
|
|
300
|
+
const region = ((resource.spec ?? {}) as { location?: string }).location ?? "us-central1";
|
|
301
|
+
out.vpcAccess = { ...vpcAccess, networkInterfaces: vpcAccess.networkInterfaces.map((i) => networkInterface(i, project, region)) };
|
|
302
|
+
}
|
|
303
|
+
body.template = out;
|
|
296
304
|
return body;
|
|
297
305
|
}
|
|
298
306
|
|
|
307
|
+
type NameRef = { name?: string; external?: string };
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* A CNRM Direct VPC egress interface as Cloud Run v2 takes it (#4249):
|
|
311
|
+
* `networkRef` and `subnetworkRef` become `network` and `subnetwork` paths,
|
|
312
|
+
* `name` in the project (the subnetwork in the service's region) and
|
|
313
|
+
* `external` as given. `tags` and anything else pass through. Pure.
|
|
314
|
+
*/
|
|
315
|
+
export function networkInterface(iface: unknown, project: string | undefined, region: string): unknown {
|
|
316
|
+
if (!iface || typeof iface !== "object") return iface;
|
|
317
|
+
const { networkRef, subnetworkRef, ...rest } = iface as { networkRef?: NameRef; subnetworkRef?: NameRef };
|
|
318
|
+
const path = (ref: NameRef | undefined, scoped: (p: string, n: string) => string, key: string): string | undefined => {
|
|
319
|
+
if (!ref) return undefined;
|
|
320
|
+
if (ref.external) return ref.external;
|
|
321
|
+
if (!ref.name) return undefined;
|
|
322
|
+
if (!project) throw new Error(`${key} { name: "${ref.name}" } needs a project to form its path`);
|
|
323
|
+
return scoped(project, ref.name);
|
|
324
|
+
};
|
|
325
|
+
const network = path(networkRef, (p, n) => `projects/${p}/global/networks/${n}`, "networkRef");
|
|
326
|
+
const subnetwork = path(subnetworkRef, (p, n) => `projects/${p}/regions/${region}/subnetworks/${n}`, "subnetworkRef");
|
|
327
|
+
return { ...rest, ...(network ? { network } : {}), ...(subnetwork ? { subnetwork } : {}) };
|
|
328
|
+
}
|
|
329
|
+
|
|
299
330
|
/**
|
|
300
331
|
* The email a CNRM `serviceAccountRef` names: `external` as given (an email,
|
|
301
332
|
* or a `projects/<p>/serviceAccounts/<email>` path), `name` as an account id
|
|
@@ -435,6 +466,8 @@ export const MAPPERS: Record<string, ResourceMapper> = {
|
|
|
435
466
|
ComputeURLMap: urlMapMapper,
|
|
436
467
|
ComputeTargetHTTPProxy: targetHttpProxyMapper,
|
|
437
468
|
ComputeForwardingRule: forwardingRuleMapper,
|
|
469
|
+
ComputeNetwork: networkMapper,
|
|
470
|
+
ComputeSubnetwork: subnetworkMapper,
|
|
438
471
|
};
|
|
439
472
|
|
|
440
473
|
/**
|
|
@@ -461,6 +494,8 @@ const REF_KINDS: Record<string, string> = {
|
|
|
461
494
|
backendServiceRef: "ComputeBackendService",
|
|
462
495
|
urlMapRef: "ComputeURLMap",
|
|
463
496
|
targetHTTPProxyRef: "ComputeTargetHTTPProxy",
|
|
497
|
+
networkRef: "ComputeNetwork",
|
|
498
|
+
subnetworkRef: "ComputeSubnetwork",
|
|
464
499
|
};
|
|
465
500
|
|
|
466
501
|
/** The local resources this resource references via `*Ref` fields, each with its kind when known. Pure. */
|
|
@@ -210,3 +210,97 @@ describe("deep observe reads a load balancer back in the declared vocabulary (#4
|
|
|
210
210
|
});
|
|
211
211
|
});
|
|
212
212
|
});
|
|
213
|
+
|
|
214
|
+
describe("network and subnetwork mappers (#4248)", () => {
|
|
215
|
+
const net: GcpResource[] = [
|
|
216
|
+
{
|
|
217
|
+
kind: "ComputeSubnetwork",
|
|
218
|
+
metadata: { name: "apps" },
|
|
219
|
+
spec: {
|
|
220
|
+
region: "europe-west1",
|
|
221
|
+
ipCidrRange: "10.0.0.0/24",
|
|
222
|
+
networkRef: { name: "vpc" },
|
|
223
|
+
privateIpGoogleAccess: true,
|
|
224
|
+
secondaryIpRange: [{ rangeName: "pods", ipCidrRange: "10.4.0.0/14" }],
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
{ kind: "ComputeNetwork", metadata: { name: "vpc" }, spec: { routingMode: "REGIONAL" } },
|
|
228
|
+
{
|
|
229
|
+
kind: "RunService",
|
|
230
|
+
metadata: { name: "web" },
|
|
231
|
+
spec: { location: "europe-west1", template: { vpcAccess: { networkInterfaces: [{ networkRef: { name: "vpc" }, subnetworkRef: { name: "apps" } }] } } },
|
|
232
|
+
},
|
|
233
|
+
];
|
|
234
|
+
const nctx = { base: "https://compute.googleapis.com", project: "p1", manifest: net };
|
|
235
|
+
const find = (kind: string) => net.find((r) => r.kind === kind)!;
|
|
236
|
+
|
|
237
|
+
test("a network is global, with autoCreateSubnetworks false unless declared and the routing mode nested", () => {
|
|
238
|
+
process.env.GCP_REGION = "us-east1";
|
|
239
|
+
const plan = MAPPERS.ComputeNetwork.plan(find("ComputeNetwork"), nctx);
|
|
240
|
+
expect(plan.create.url).toBe("https://compute.googleapis.com/compute/v1/projects/p1/global/networks");
|
|
241
|
+
expect(plan.getUrl).toBe("https://compute.googleapis.com/compute/v1/projects/p1/global/networks/vpc");
|
|
242
|
+
expect(plan.create.body).toEqual({ name: "vpc", autoCreateSubnetworks: false, routingConfig: { routingMode: "REGIONAL" } });
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("a subnetwork is regional and names its network by partial URL", () => {
|
|
246
|
+
const plan = MAPPERS.ComputeSubnetwork.plan(find("ComputeSubnetwork"), nctx);
|
|
247
|
+
expect(plan.create.url).toBe("https://compute.googleapis.com/compute/v1/projects/p1/regions/europe-west1/subnetworks");
|
|
248
|
+
expect(plan.create.body).toEqual({
|
|
249
|
+
name: "apps",
|
|
250
|
+
ipCidrRange: "10.0.0.0/24",
|
|
251
|
+
privateIpGoogleAccess: true,
|
|
252
|
+
network: "projects/p1/global/networks/vpc",
|
|
253
|
+
secondaryIpRanges: [{ rangeName: "pods", ipCidrRange: "10.4.0.0/14" }],
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("apply order: network, then subnetwork, then the Cloud Run service that egresses into them", () => {
|
|
258
|
+
const order = orderByReferences(net).map((r) => r.kind);
|
|
259
|
+
expect(order.indexOf("ComputeNetwork")).toBeLessThan(order.indexOf("ComputeSubnetwork"));
|
|
260
|
+
expect(order.indexOf("ComputeSubnetwork")).toBeLessThan(order.indexOf("RunService"));
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("the reader reads a network at its global URL", async () => {
|
|
264
|
+
const http = async (_m: string, url: string) =>
|
|
265
|
+
url.endsWith("/global/networks/vpc") ? { status: 200, text: JSON.stringify({ name: "vpc" }) } : { status: 404, text: "" };
|
|
266
|
+
expect(await getResource({ project: "p1", http }, "ComputeNetwork", "vpc", {})).toEqual({ name: "vpc" });
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("deep observe inverts both body builders", async () => {
|
|
270
|
+
const { toCnrmTree } = await import("../../deep-observe");
|
|
271
|
+
const c = { project: "p1" };
|
|
272
|
+
expect(
|
|
273
|
+
toCnrmTree(
|
|
274
|
+
"ComputeNetwork",
|
|
275
|
+
{
|
|
276
|
+
name: "vpc",
|
|
277
|
+
autoCreateSubnetworks: false,
|
|
278
|
+
routingConfig: { routingMode: "REGIONAL" },
|
|
279
|
+
subnetworks: ["https://compute.googleapis.com/compute/v1/projects/p1/regions/europe-west1/subnetworks/apps"],
|
|
280
|
+
},
|
|
281
|
+
c,
|
|
282
|
+
),
|
|
283
|
+
).toEqual({ metadata: { name: "vpc" }, autoCreateSubnetworks: false, routingMode: "REGIONAL" });
|
|
284
|
+
expect(
|
|
285
|
+
toCnrmTree(
|
|
286
|
+
"ComputeSubnetwork",
|
|
287
|
+
{
|
|
288
|
+
name: "apps",
|
|
289
|
+
region: "https://compute.googleapis.com/compute/v1/projects/p1/regions/europe-west1",
|
|
290
|
+
network: "https://compute.googleapis.com/compute/v1/projects/p1/global/networks/vpc",
|
|
291
|
+
ipCidrRange: "10.0.0.0/24",
|
|
292
|
+
gatewayAddress: "10.0.0.1",
|
|
293
|
+
fingerprint: "abc",
|
|
294
|
+
secondaryIpRanges: [{ rangeName: "pods", ipCidrRange: "10.4.0.0/14" }],
|
|
295
|
+
},
|
|
296
|
+
c,
|
|
297
|
+
),
|
|
298
|
+
).toEqual({
|
|
299
|
+
metadata: { name: "apps" },
|
|
300
|
+
region: "europe-west1",
|
|
301
|
+
networkRef: { name: "vpc" },
|
|
302
|
+
ipCidrRange: "10.0.0.0/24",
|
|
303
|
+
secondaryIpRange: [{ rangeName: "pods", ipCidrRange: "10.4.0.0/14" }],
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
});
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compute Engine mappers for the Cloud Run load balancer (#4233): ComputeAddress,
|
|
3
3
|
* ComputeRegionNetworkEndpointGroup (serverless NEGs), ComputeBackendService,
|
|
4
|
-
* ComputeURLMap, ComputeTargetHTTPProxy and ComputeForwardingRule
|
|
4
|
+
* ComputeURLMap, ComputeTargetHTTPProxy and ComputeForwardingRule; and for a
|
|
5
|
+
* declared network (#4248): ComputeNetwork (always global) and
|
|
6
|
+
* ComputeSubnetwork (regional, by its CNRM `region`).
|
|
5
7
|
*
|
|
6
8
|
* Each maps a CNRM spec onto the compute v1 insert body. A resource is global
|
|
7
9
|
* or regional by its CNRM `location` (`"global"` or a region; a NEG has
|
|
@@ -168,6 +170,36 @@ export function negBody(resource: GcpResource, ctx: ComputeCtx): Obj {
|
|
|
168
170
|
return body;
|
|
169
171
|
}
|
|
170
172
|
|
|
173
|
+
/**
|
|
174
|
+
* ComputeNetwork → networks.insert body. A network is global. CNRM's
|
|
175
|
+
* `autoCreateSubnetworks` defaults to false, where compute's default is true,
|
|
176
|
+
* so it is always sent. Pure.
|
|
177
|
+
*/
|
|
178
|
+
export function networkBody(resource: GcpResource): Obj {
|
|
179
|
+
const spec = (resource.spec ?? {}) as Obj;
|
|
180
|
+
const body: Obj = {
|
|
181
|
+
name: computeName(resource),
|
|
182
|
+
autoCreateSubnetworks: spec.autoCreateSubnetworks === true,
|
|
183
|
+
...pick(spec, ["description", "mtu", "networkFirewallPolicyEnforcementOrder"]),
|
|
184
|
+
};
|
|
185
|
+
if (typeof spec.routingMode === "string") body.routingConfig = { routingMode: spec.routingMode };
|
|
186
|
+
return body;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** ComputeSubnetwork → subnetworks.insert body. CNRM's `secondaryIpRange` and `logConfig` become compute's `secondaryIpRanges` and an enabled `logConfig`. Pure. */
|
|
190
|
+
export function subnetworkBody(resource: GcpResource, ctx: ComputeCtx): Obj {
|
|
191
|
+
const spec = (resource.spec ?? {}) as Obj;
|
|
192
|
+
const scope = scopeOf(spec);
|
|
193
|
+
const body: Obj = {
|
|
194
|
+
name: computeName(resource),
|
|
195
|
+
...pick(spec, ["ipCidrRange", "description", "privateIpGoogleAccess", "purpose", "role", "stackType"]),
|
|
196
|
+
};
|
|
197
|
+
if (isObj(spec.networkRef)) body.network = refUrl("networkRef", spec.networkRef as Ref, ctx, scope);
|
|
198
|
+
if (Array.isArray(spec.secondaryIpRange)) body.secondaryIpRanges = spec.secondaryIpRange;
|
|
199
|
+
if (isObj(spec.logConfig)) body.logConfig = { enable: true, ...spec.logConfig };
|
|
200
|
+
return body;
|
|
201
|
+
}
|
|
202
|
+
|
|
171
203
|
/** ComputeBackendService → backendServices.insert body. Pure. */
|
|
172
204
|
export function backendServiceBody(resource: GcpResource, ctx: ComputeCtx): Obj {
|
|
173
205
|
const spec = (resource.spec ?? {}) as Obj;
|
|
@@ -297,13 +329,14 @@ function computeMapper(
|
|
|
297
329
|
collection: string,
|
|
298
330
|
build: (resource: GcpResource, ctx: ComputeCtx) => Obj,
|
|
299
331
|
regionalCollection = collection,
|
|
332
|
+
scopeFor: (spec: Obj | undefined) => string = (spec) => scopeOf(spec),
|
|
300
333
|
): ResourceMapper {
|
|
301
334
|
return {
|
|
302
335
|
kind,
|
|
303
336
|
defaultHost: COMPUTE_HOST,
|
|
304
337
|
operation: computeOperation,
|
|
305
338
|
plan(resource, ctx) {
|
|
306
|
-
const scope =
|
|
339
|
+
const scope = scopeFor(resource.spec as Obj | undefined);
|
|
307
340
|
const coll = `${ctx.base}/compute/v1/${scopePath(ctx.project, scope)}/${scope === "global" ? collection : regionalCollection}`;
|
|
308
341
|
const name = computeName(resource);
|
|
309
342
|
return { getUrl: `${coll}/${enc(name)}`, create: { method: "POST", url: coll, body: build(resource, ctx) } };
|
|
@@ -317,3 +350,5 @@ export const backendServiceMapper = computeMapper("ComputeBackendService", "back
|
|
|
317
350
|
export const urlMapMapper = computeMapper("ComputeURLMap", "urlMaps", urlMapBody);
|
|
318
351
|
export const targetHttpProxyMapper = computeMapper("ComputeTargetHTTPProxy", "targetHttpProxies", targetHttpProxyBody);
|
|
319
352
|
export const forwardingRuleMapper = computeMapper("ComputeForwardingRule", "forwardingRules", forwardingRuleBody);
|
|
353
|
+
export const networkMapper = computeMapper("ComputeNetwork", "networks", networkBody, "networks", () => "global");
|
|
354
|
+
export const subnetworkMapper = computeMapper("ComputeSubnetwork", "subnetworks", subnetworkBody);
|
package/src/serializer.test.ts
CHANGED
|
@@ -293,3 +293,29 @@ describe("gcpSerializer", () => {
|
|
|
293
293
|
expect(result).toContain("ComputeInstance");
|
|
294
294
|
});
|
|
295
295
|
});
|
|
296
|
+
|
|
297
|
+
describe("a .name reference serializes as the target's metadata.name (#4242)", () => {
|
|
298
|
+
test("a declared metadata.name wins; without one, the kebab-cased logical id", async () => {
|
|
299
|
+
const { RegionNetworkEndpointGroup, BackendService, CloudRunService } = await import("./generated");
|
|
300
|
+
const web = new CloudRunService({ location: "us-central1", template: { containers: [{ image: "i" }] } } as any);
|
|
301
|
+
(web as any).name._setLogicalName("webService");
|
|
302
|
+
const neg = new RegionNetworkEndpointGroup({
|
|
303
|
+
metadata: { name: "three-cloud-neg" },
|
|
304
|
+
region: "us-central1",
|
|
305
|
+
networkEndpointType: "SERVERLESS",
|
|
306
|
+
cloudRun: { serviceRef: { name: (web as any).name } },
|
|
307
|
+
} as any);
|
|
308
|
+
(neg as any).name._setLogicalName("neg");
|
|
309
|
+
const backend = new BackendService({
|
|
310
|
+
metadata: { name: "three-cloud-backend" },
|
|
311
|
+
location: "global",
|
|
312
|
+
backend: [{ group: { networkEndpointGroupRef: { name: (neg as any).name } } }],
|
|
313
|
+
} as any);
|
|
314
|
+
const entities = new Map<string, any>([["webService", web], ["neg", neg], ["backend", backend]]);
|
|
315
|
+
const yaml = String(gcpSerializer.serialize(entities));
|
|
316
|
+
expect(yaml).toContain("name: three-cloud-neg");
|
|
317
|
+
expect(yaml).not.toMatch(/networkEndpointGroupRef:\s*\n\s*name: neg\b/);
|
|
318
|
+
expect(yaml).toMatch(/serviceRef:\s*\n\s*name: web-service/);
|
|
319
|
+
expect(yaml).toMatch(/networkEndpointGroupRef:\s*\n\s*name: three-cloud-neg/);
|
|
320
|
+
});
|
|
321
|
+
});
|
package/src/serializer.ts
CHANGED
|
@@ -84,15 +84,14 @@ function deriveGVKFromType(entityType: string): { apiVersion: string; kind: stri
|
|
|
84
84
|
* GCP visitor for the generic serializer walker.
|
|
85
85
|
*/
|
|
86
86
|
function gcpVisitor(entityNames: Map<Declarable, string>): SerializerVisitor {
|
|
87
|
+
// Config Connector resolves a `*Ref: { name }` by the target's metadata.name,
|
|
88
|
+
// which is its declared name or else the kebab-cased logical id (#4242).
|
|
89
|
+
const cnrmNames = new Map<string, string>();
|
|
90
|
+
for (const [entity, logical] of entityNames) cnrmNames.set(logical, cnrmName(logical, entity));
|
|
91
|
+
const resolve = (logical: string) => cnrmNames.get(logical) ?? logical;
|
|
87
92
|
return {
|
|
88
|
-
attrRef: (name, _attr) =>
|
|
89
|
-
|
|
90
|
-
return name;
|
|
91
|
-
},
|
|
92
|
-
resourceRef: (name) => {
|
|
93
|
-
// Config Connector uses resourceRef pattern: { name: "resource-name" }
|
|
94
|
-
return { name };
|
|
95
|
-
},
|
|
93
|
+
attrRef: (name, _attr) => resolve(name),
|
|
94
|
+
resourceRef: (name) => ({ name: resolve(name) }),
|
|
96
95
|
propertyDeclarable: (entity, walk) => {
|
|
97
96
|
if (!isResourceDeclarable(entity) || typeof entity.props !== "object" || entity.props === null) {
|
|
98
97
|
return undefined;
|
|
@@ -109,6 +108,20 @@ function gcpVisitor(entityNames: Map<Declarable, string>): SerializerVisitor {
|
|
|
109
108
|
};
|
|
110
109
|
}
|
|
111
110
|
|
|
111
|
+
/**
|
|
112
|
+
* The metadata.name a resource is written with: a string `metadata.name` in its
|
|
113
|
+
* props, else its logical id in kebab case.
|
|
114
|
+
*/
|
|
115
|
+
export function cnrmName(logical: string, entity: Declarable): string {
|
|
116
|
+
const props = isResourceDeclarable(entity) ? (entity.props as { metadata?: { name?: unknown } } | undefined) : undefined;
|
|
117
|
+
const declared = props?.metadata?.name;
|
|
118
|
+
return typeof declared === "string" && declared.length > 0 ? declared : kebabName(logical);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function kebabName(logical: string): string {
|
|
122
|
+
return logical.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
123
|
+
}
|
|
124
|
+
|
|
112
125
|
/**
|
|
113
126
|
* Convert a value to YAML-compatible form using the walker.
|
|
114
127
|
*/
|
|
@@ -189,7 +202,7 @@ export const gcpSerializer: Serializer = {
|
|
|
189
202
|
// Build metadata
|
|
190
203
|
const metadata: Record<string, unknown> = props.metadata as Record<string, unknown> ?? {};
|
|
191
204
|
if (!metadata.name) {
|
|
192
|
-
metadata.name = name
|
|
205
|
+
metadata.name = kebabName(name);
|
|
193
206
|
}
|
|
194
207
|
|
|
195
208
|
// Merge default labels
|