@intentius/chant-lexicon-gcp 0.124.36 → 0.124.37
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/deep-observe.d.ts.map +1 -1
- package/dist/deep-observe.js +4 -11
- package/dist/describe-resources.d.ts +20 -6
- package/dist/describe-resources.d.ts.map +1 -1
- package/dist/describe-resources.js +40 -26
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js +6 -9
- package/dist/integrity.json +2 -2
- package/dist/manifest.json +1 -1
- package/dist/op/activities/gcp-apply.d.ts +7 -1
- package/dist/op/activities/gcp-apply.d.ts.map +1 -1
- package/dist/op/activities/gcp-apply.js +12 -6
- package/dist/op/activities/gcp-iam.d.ts.map +1 -1
- package/dist/op/activities/gcp-iam.js +2 -0
- package/dist/serializer.d.ts +12 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/serializer.js +52 -8
- package/package.json +2 -2
- package/src/deep-observe.ts +5 -12
- package/src/describe-resources.test.ts +5 -4
- package/src/describe-resources.ts +43 -24
- package/src/health.ts +7 -10
- package/src/op/activities/gcp-apply.test.ts +10 -3
- package/src/op/activities/gcp-apply.ts +13 -8
- package/src/op/activities/gcp-iam.ts +2 -0
- package/src/project-config.test.ts +119 -0
- package/src/read-refs.test.ts +135 -0
- package/src/serializer.ts +53 -8
|
@@ -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;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,
|
|
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,CAuF5G"}
|
package/dist/deep-observe.js
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
*/
|
|
56
56
|
import { deepObservation, normalizeDeepProperties } from "@intentius/chant/deep-observation";
|
|
57
57
|
import { hasOwnershipMarker } from "@intentius/chant/ownership";
|
|
58
|
-
import { deriveGVK, manifestProjectAnnotations, resolveReadProject } from "./describe-resources.js";
|
|
58
|
+
import { deriveGVK, manifestProjectAnnotations, readTargets, resolveReadProject } from "./describe-resources.js";
|
|
59
59
|
import { getResource, mapperForKind, GcpReadError, isNotFound } from "./api/read-client.js";
|
|
60
60
|
import { gcpDeepNormalizationHooks } from "./deep-observe-hooks.js";
|
|
61
61
|
/** The labels the applier stamps — see describe-resources.ts. */
|
|
@@ -364,7 +364,8 @@ export async function observeResourcesDeepGcp(options) {
|
|
|
364
364
|
const unobserved = {};
|
|
365
365
|
const endpoint = process.env.GCP_ENDPOINT_URL;
|
|
366
366
|
const manifestProjects = manifestProjectAnnotations(options.buildOutput);
|
|
367
|
-
const
|
|
367
|
+
const targets = readTargets(options.entities);
|
|
368
|
+
const reads = [...options.entities].map(async ([entityName, { entityType }]) => {
|
|
368
369
|
const gvk = deriveGVK(entityType);
|
|
369
370
|
if (!gvk || !mapperForKind(gvk.kind)) {
|
|
370
371
|
unobserved[entityName] = {
|
|
@@ -376,16 +377,8 @@ export async function observeResourcesDeepGcp(options) {
|
|
|
376
377
|
};
|
|
377
378
|
return;
|
|
378
379
|
}
|
|
380
|
+
const { name, props } = targets.get(entityName);
|
|
379
381
|
const metadata = props.metadata;
|
|
380
|
-
const name = metadata?.name;
|
|
381
|
-
if (!name) {
|
|
382
|
-
unobserved[entityName] = {
|
|
383
|
-
type: entityType,
|
|
384
|
-
reason: "read-failed",
|
|
385
|
-
detail: "declared entity has no metadata.name to query by",
|
|
386
|
-
};
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
382
|
let client;
|
|
390
383
|
try {
|
|
391
384
|
client = {
|
|
@@ -82,15 +82,29 @@ export declare function deriveGVK(entityType: string): {
|
|
|
82
82
|
*/
|
|
83
83
|
export declare function manifestProjectAnnotations(buildOutput: string | undefined): Map<string, string>;
|
|
84
84
|
/**
|
|
85
|
-
* The project a read should target: the entity's
|
|
86
|
-
* `
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
85
|
+
* The project a read should target, in apply's order (#4252): the entity's
|
|
86
|
+
* own literal `projectRef.external`, then the built manifest's project for it
|
|
87
|
+
* (its resolved projectRef, or the project-id annotation chant.config's
|
|
88
|
+
* `gcp.project` writes), then the entity's own annotation, then
|
|
89
|
+
* `GCP_PROJECT_ID`, then `GOOGLE_CLOUD_PROJECT`. Throws apply's own error when
|
|
90
|
+
* nothing resolves, so the `no-binding` detail names every knob.
|
|
90
91
|
*/
|
|
91
92
|
export declare function resolveReadProject(kind: string, name: string, metadata: {
|
|
92
93
|
annotations?: Record<string, string>;
|
|
93
|
-
} | undefined, manifestProjects: Map<string, string>, props?: Record<string, unknown
|
|
94
|
+
} | undefined, manifestProjects: Map<string, string>, props?: Record<string, unknown>, env?: NodeJS.ProcessEnv): string;
|
|
95
|
+
/**
|
|
96
|
+
* Each declared entity as a read sees it (#4251): the name the build writes
|
|
97
|
+
* (`metadata.name`, else the kebab-cased logical id) and its props with every
|
|
98
|
+
* reference to another resource resolved as the serializer resolves it, so a
|
|
99
|
+
* mapper building a read URL never meets an attribute reference.
|
|
100
|
+
*/
|
|
101
|
+
export declare function readTargets(entities: Map<string, {
|
|
102
|
+
entityType: string;
|
|
103
|
+
props: Record<string, unknown>;
|
|
104
|
+
}>): Map<string, {
|
|
105
|
+
name: string;
|
|
106
|
+
props: Record<string, unknown>;
|
|
107
|
+
}>;
|
|
94
108
|
/**
|
|
95
109
|
* Status from a REST payload.
|
|
96
110
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-resources.d.ts","sourceRoot":"","sources":["../src/describe-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAsC,MAAM,0BAA0B,CAAC;AAsBtG;;gCAEgC;AAChC,UAAU,eAAe;IACvB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACxF,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;KACtB,CAAC;CACH;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAIpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAgB/F;AAED
|
|
1
|
+
{"version":3,"file":"describe-resources.d.ts","sourceRoot":"","sources":["../src/describe-resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAsC,MAAM,0BAA0B,CAAC;AAsBtG;;gCAEgC;AAChC,UAAU,eAAe;IACvB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,KAAK,CAAC;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACxF,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;KACtB,CAAC;CACH;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAIpF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAgB/F;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,SAAS,EAC9D,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAUR;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GAC5E,GAAG,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAY/D;AAUD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,MAAM,CAY3D;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,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,OAAO,CAAC;CACjB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAiH7B"}
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
import { observation } from "@intentius/chant/observation";
|
|
39
39
|
import { hasOwnershipMarker, classifyOwnership, readOwnership } from "@intentius/chant/ownership";
|
|
40
40
|
import { getResource, mapperForKind, GcpReadError, isNotFound } from "./api/read-client.js";
|
|
41
|
-
import {
|
|
42
|
-
import { resolveGVK } from "./serializer.js";
|
|
41
|
+
import { NO_PROJECT, parseManifest, projectRefProject } from "./op/activities/gcp-apply.js";
|
|
42
|
+
import { cnrmNameOf, resolveCnrmRefs, resolveGVK } from "./serializer.js";
|
|
43
43
|
/**
|
|
44
44
|
* chant's ownership marker as GCP labels.
|
|
45
45
|
*
|
|
@@ -99,22 +99,43 @@ export function manifestProjectAnnotations(buildOutput) {
|
|
|
99
99
|
return out;
|
|
100
100
|
}
|
|
101
101
|
/**
|
|
102
|
-
* The project a read should target: the entity's
|
|
103
|
-
* `
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
102
|
+
* The project a read should target, in apply's order (#4252): the entity's
|
|
103
|
+
* own literal `projectRef.external`, then the built manifest's project for it
|
|
104
|
+
* (its resolved projectRef, or the project-id annotation chant.config's
|
|
105
|
+
* `gcp.project` writes), then the entity's own annotation, then
|
|
106
|
+
* `GCP_PROJECT_ID`, then `GOOGLE_CLOUD_PROJECT`. Throws apply's own error when
|
|
107
|
+
* nothing resolves, so the `no-binding` detail names every knob.
|
|
107
108
|
*/
|
|
108
|
-
export function resolveReadProject(kind, name, metadata, manifestProjects, props) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
throw
|
|
109
|
+
export function resolveReadProject(kind, name, metadata, manifestProjects, props, env = process.env) {
|
|
110
|
+
const project = projectRefProject(props, {}) ||
|
|
111
|
+
manifestProjects.get(`${kind}/${name}`) ||
|
|
112
|
+
metadata?.annotations?.["cnrm.cloud.google.com/project-id"] ||
|
|
113
|
+
projectRefProject(props, env) ||
|
|
114
|
+
env.GCP_PROJECT_ID ||
|
|
115
|
+
env.GOOGLE_CLOUD_PROJECT;
|
|
116
|
+
if (!project)
|
|
117
|
+
throw new Error(NO_PROJECT);
|
|
118
|
+
return project;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Each declared entity as a read sees it (#4251): the name the build writes
|
|
122
|
+
* (`metadata.name`, else the kebab-cased logical id) and its props with every
|
|
123
|
+
* reference to another resource resolved as the serializer resolves it, so a
|
|
124
|
+
* mapper building a read URL never meets an attribute reference.
|
|
125
|
+
*/
|
|
126
|
+
export function readTargets(entities) {
|
|
127
|
+
const logicalByProps = new Map();
|
|
128
|
+
for (const [logical, { props }] of entities)
|
|
129
|
+
logicalByProps.set(props, logical);
|
|
130
|
+
const logicalOf = (props) => logicalByProps.get(props);
|
|
131
|
+
const out = new Map();
|
|
132
|
+
for (const [logical, { props }] of entities) {
|
|
133
|
+
out.set(logical, {
|
|
134
|
+
name: cnrmNameOf(logical, props),
|
|
135
|
+
props: resolveCnrmRefs(props, logicalOf),
|
|
136
|
+
});
|
|
117
137
|
}
|
|
138
|
+
return out;
|
|
118
139
|
}
|
|
119
140
|
function pruneUndefined(obj) {
|
|
120
141
|
const out = {};
|
|
@@ -163,7 +184,8 @@ export async function describeResources(options) {
|
|
|
163
184
|
// `describe-stack-resources` returns no tags.
|
|
164
185
|
let warnedOwnership = false;
|
|
165
186
|
const manifestProjects = manifestProjectAnnotations(options.buildOutput);
|
|
166
|
-
const
|
|
187
|
+
const targets = readTargets(options.entities);
|
|
188
|
+
const reads = [...options.entities].map(async ([entityName, { entityType }]) => {
|
|
167
189
|
const gvk = deriveGVK(entityType);
|
|
168
190
|
if (!gvk) {
|
|
169
191
|
unobserved[entityName] = {
|
|
@@ -183,16 +205,8 @@ export async function describeResources(options) {
|
|
|
183
205
|
};
|
|
184
206
|
return;
|
|
185
207
|
}
|
|
208
|
+
const { name, props } = targets.get(entityName);
|
|
186
209
|
const metadata = props.metadata;
|
|
187
|
-
const name = metadata?.name;
|
|
188
|
-
if (!name) {
|
|
189
|
-
unobserved[entityName] = {
|
|
190
|
-
type: entityType,
|
|
191
|
-
reason: "read-failed",
|
|
192
|
-
detail: "declared entity has no metadata.name to query by",
|
|
193
|
-
};
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
210
|
let client;
|
|
197
211
|
try {
|
|
198
212
|
client = {
|
package/dist/health.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAoB,MAAM,0BAA0B,CAAC;AACnH,OAAO,EAAyC,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG5F,eAAO,MAAM,gBAAgB,YAAI,mBAAmB,CAAU,CAAC;AAE/D,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,UAAU,SAAS;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,gEAAgE;AAChE,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD;AAOD,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,CAqBzF;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAoB,MAAM,0BAA0B,CAAC;AACnH,OAAO,EAAyC,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG5F,eAAO,MAAM,gBAAgB,YAAI,mBAAmB,CAAU,CAAC;AAE/D,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,UAAU,SAAS;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,gEAAgE;AAChE,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC9B,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD;AAOD,yEAAyE;AACzE,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,CAqBzF;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAiD3F"}
|
package/dist/health.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* `GCP_ENDPOINT_URL` (floci-gcp) answers.
|
|
10
10
|
*/
|
|
11
11
|
import { getResource, GcpReadError, isNotFound } from "./api/read-client.js";
|
|
12
|
-
import { deriveGVK, manifestProjectAnnotations, resolveReadProject } from "./describe-resources.js";
|
|
12
|
+
import { deriveGVK, manifestProjectAnnotations, readTargets, resolveReadProject } from "./describe-resources.js";
|
|
13
13
|
export const GCP_HEALTH_TYPES = ["GCP::Run::Service"];
|
|
14
14
|
function readyCondition(body) {
|
|
15
15
|
if (body.terminalCondition && (body.terminalCondition.type ?? "Ready") === "Ready")
|
|
@@ -46,16 +46,13 @@ export async function observeHealthGcp(options) {
|
|
|
46
46
|
const entities = {};
|
|
47
47
|
const unobserved = {};
|
|
48
48
|
const absent = [];
|
|
49
|
-
|
|
49
|
+
const targets = readTargets(options.entities);
|
|
50
|
+
await Promise.all([...options.entities].map(async ([entityName, { entityType }]) => {
|
|
50
51
|
const gvk = deriveGVK(entityType);
|
|
52
|
+
const { name, props } = targets.get(entityName);
|
|
51
53
|
const metadata = props.metadata;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
unobserved[entityName] = {
|
|
55
|
-
type: entityType,
|
|
56
|
-
reason: "read-failed",
|
|
57
|
-
detail: gvk ? "declared entity has no metadata.name to query by" : `cannot derive a GCP kind from ${entityType}`,
|
|
58
|
-
};
|
|
54
|
+
if (!gvk) {
|
|
55
|
+
unobserved[entityName] = { type: entityType, reason: "read-failed", detail: `cannot derive a GCP kind from ${entityType}` };
|
|
59
56
|
return;
|
|
60
57
|
}
|
|
61
58
|
let project;
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "648e7fb96c2a44b72459dc70e777c22836de776eb5f5b8017f8c6057135062a3",
|
|
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": "12781f2fbe08546cbfed0496ef97a0cfc6f301c5576bfafad2cbd4bf3cac39f3"
|
|
44
44
|
}
|
package/dist/manifest.json
CHANGED
|
@@ -249,7 +249,6 @@ export declare function orderByReferences(resources: GcpResource[]): GcpResource
|
|
|
249
249
|
export declare function resolveGcpEndpoint(args?: {
|
|
250
250
|
endpoint?: string;
|
|
251
251
|
}, env?: NodeJS.ProcessEnv): string | undefined;
|
|
252
|
-
/** Resolve the project: `GOOGLE_CLOUD_PROJECT` env, else the CNRM project-id annotation. Pure. */
|
|
253
252
|
/**
|
|
254
253
|
* The project a resource names in `spec.projectRef.external` (#4201): a
|
|
255
254
|
* project id or `projects/<id>`. A `GCP.ProjectId` pseudo-parameter there
|
|
@@ -257,7 +256,14 @@ export declare function resolveGcpEndpoint(args?: {
|
|
|
257
256
|
* `GCP_PROJECT_ID`, as the serializer does.
|
|
258
257
|
*/
|
|
259
258
|
export declare function projectRefProject(spec: Record<string, unknown> | undefined, env?: NodeJS.ProcessEnv): string | undefined;
|
|
259
|
+
/**
|
|
260
|
+
* The project a resource is applied in (#4252): its own `spec.projectRef.external`,
|
|
261
|
+
* else its project-id annotation (where chant.config's `gcp.project` is
|
|
262
|
+
* written at build), else `GCP_PROJECT_ID`, else `GOOGLE_CLOUD_PROJECT`. Pure.
|
|
263
|
+
*/
|
|
260
264
|
export declare function resolveGcpProject(resource: GcpResource, env?: NodeJS.ProcessEnv): string;
|
|
265
|
+
/** The error every stage gives when no project resolves. */
|
|
266
|
+
export declare const NO_PROJECT = "no GCP project \u2014 set gcp.project in chant.config, GCP_PROJECT_ID or GOOGLE_CLOUD_PROJECT, or the resource's spec.projectRef.external";
|
|
261
267
|
/** Injectable HTTP client — mirrors argo's injectable fetcher so tests avoid the network. */
|
|
262
268
|
export type GcpHttp = (method: string, url: string, body?: unknown, signal?: AbortSignal) => Promise<{
|
|
263
269
|
status: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcp-apply.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-apply.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"gcp-apply.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -425,7 +425,6 @@ export function orderByReferences(resources) {
|
|
|
425
425
|
export function resolveGcpEndpoint(args = {}, env = process.env) {
|
|
426
426
|
return args.endpoint || env.GCP_ENDPOINT_URL || undefined;
|
|
427
427
|
}
|
|
428
|
-
/** Resolve the project: `GOOGLE_CLOUD_PROJECT` env, else the CNRM project-id annotation. Pure. */
|
|
429
428
|
/**
|
|
430
429
|
* The project a resource names in `spec.projectRef.external` (#4201): a
|
|
431
430
|
* project id or `projects/<id>`. A `GCP.ProjectId` pseudo-parameter there
|
|
@@ -447,15 +446,22 @@ export function projectRefProject(spec, env = process.env) {
|
|
|
447
446
|
return undefined;
|
|
448
447
|
return external.startsWith("projects/") ? external.slice("projects/".length).split("/")[0] : external;
|
|
449
448
|
}
|
|
449
|
+
/**
|
|
450
|
+
* The project a resource is applied in (#4252): its own `spec.projectRef.external`,
|
|
451
|
+
* else its project-id annotation (where chant.config's `gcp.project` is
|
|
452
|
+
* written at build), else `GCP_PROJECT_ID`, else `GOOGLE_CLOUD_PROJECT`. Pure.
|
|
453
|
+
*/
|
|
450
454
|
export function resolveGcpProject(resource, env = process.env) {
|
|
451
455
|
const project = projectRefProject(resource.spec, env) ||
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
456
|
+
resource.metadata?.annotations?.[PROJECT_ID_ANNOTATION] ||
|
|
457
|
+
env.GCP_PROJECT_ID ||
|
|
458
|
+
env.GOOGLE_CLOUD_PROJECT;
|
|
459
|
+
if (!project)
|
|
460
|
+
throw new Error(NO_PROJECT);
|
|
457
461
|
return project;
|
|
458
462
|
}
|
|
463
|
+
/** The error every stage gives when no project resolves. */
|
|
464
|
+
export const NO_PROJECT = `no GCP project — set gcp.project in chant.config, GCP_PROJECT_ID or GOOGLE_CLOUD_PROJECT, or the resource's spec.projectRef.external`;
|
|
459
465
|
const defaultHttp = async (method, url, body, signal) => {
|
|
460
466
|
const res = await fetch(url, {
|
|
461
467
|
method,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gcp-iam.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-iam.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAW,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAExE,iDAAiD;AACjD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvF,SAAS,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,OAAO;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E;AAED,UAAU,MAAM;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,mDAAmD;AACnD,MAAM,WAAW,SAAS;IACxB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/E,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK;QAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;CAC/F;AAED,gHAAgH;AAChH,eAAO,MAAM,gBAAgB,+BAA+B,CAAC;AAqB7D;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,SAAS,WAAW,EAAO,EACrC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,SAAS,GAAG,SAAS,CAgDvB;AAYD,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAElF;AAED,8FAA8F;AAC9F,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAY5E;AAED,kGAAkG;AAClG,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAS/E;AAkED,eAAO,MAAM,qBAAqB,EAAE,
|
|
1
|
+
{"version":3,"file":"gcp-iam.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-iam.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAW,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAExE,iDAAiD;AACjD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvF,SAAS,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,OAAO;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E;AAED,UAAU,MAAM;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,mDAAmD;AACnD,MAAM,WAAW,SAAS;IACxB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IAC/E,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK;QAAE,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;CAC/F;AAED,gHAAgH;AAChH,eAAO,MAAM,gBAAgB,+BAA+B,CAAC;AAqB7D;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,SAAS,WAAW,EAAO,EACrC,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,SAAS,GAAG,SAAS,CAgDvB;AAYD,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAElF;AAED,8FAA8F;AAC9F,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAY5E;AAED,kGAAkG;AAClG,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAS/E;AAkED,eAAO,MAAM,qBAAqB,EAAE,cAgDnC,CAAC"}
|
|
@@ -202,6 +202,8 @@ export const iamPolicyMemberMapper = {
|
|
|
202
202
|
status: 200,
|
|
203
203
|
// `resourceRef` is the declared one: the policy read is that target's, so the binding is on it.
|
|
204
204
|
text: JSON.stringify({
|
|
205
|
+
// The binding has no id of its own; this one names it, so a read reports a physical id (#4251).
|
|
206
|
+
selfLink: `${target.path}#${spec.role}#${spec.member}`,
|
|
205
207
|
member: spec.member,
|
|
206
208
|
role: spec.role,
|
|
207
209
|
resource: target.path,
|
package/dist/serializer.d.ts
CHANGED
|
@@ -23,8 +23,20 @@ export declare function resolveGVK(entityType: string): {
|
|
|
23
23
|
* props, else its logical id in kebab case.
|
|
24
24
|
*/
|
|
25
25
|
export declare function cnrmName(logical: string, entity: Declarable): string;
|
|
26
|
+
/** {@link cnrmName} from a resource's props, for callers holding props rather than the declarable. */
|
|
27
|
+
export declare function cnrmNameOf(logical: string, props: unknown): string;
|
|
28
|
+
/**
|
|
29
|
+
* Declared props with each reference to another resource replaced as the
|
|
30
|
+
* serializer writes it (#4251): an attribute reference (`neg.name`) becomes the
|
|
31
|
+
* target's metadata.name, and a resource placed whole becomes `{ name }`.
|
|
32
|
+
* `logicalOf` maps a target's props object to its logical id, for a reference
|
|
33
|
+
* that does not carry one. Other values, pseudo-parameters included, are kept.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveCnrmRefs(value: unknown, logicalOf: (props: unknown) => string | undefined): unknown;
|
|
26
36
|
/**
|
|
27
37
|
* GCP Config Connector YAML serializer implementation.
|
|
28
38
|
*/
|
|
29
39
|
export declare const gcpSerializer: Serializer;
|
|
40
|
+
/** The `gcp.project` a chant.config declares, if any (#4252). */
|
|
41
|
+
export declare function gcpConfigProject(config: Record<string, unknown> | undefined): string | undefined;
|
|
30
42
|
//# sourceMappingURL=serializer.d.ts.map
|
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;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;
|
|
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;AAG9D,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,CAEpE;AAED,sGAAsG;AACtG,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAGlE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAkB1G;AA4BD;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAsG3B,CAAC;AAIF,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGhG"}
|
package/dist/serializer.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
import { moduleSourceUrl } from "@intentius/chant/module-source";
|
|
9
|
-
import { isPropertyDeclarable, isResourceDeclarable } from "@intentius/chant/declarable";
|
|
9
|
+
import { isDeclarable, isPropertyDeclarable, isResourceDeclarable } from "@intentius/chant/declarable";
|
|
10
|
+
import { AttrRef } from "@intentius/chant/attrref";
|
|
10
11
|
import { ownershipEntries, LABEL_OWNERSHIP_KEYS } from "@intentius/chant/ownership";
|
|
11
12
|
import { walkValue } from "@intentius/chant/serializer-walker";
|
|
12
13
|
import { emitYAML } from "@intentius/chant/yaml";
|
|
@@ -91,10 +92,43 @@ function gcpVisitor(entityNames) {
|
|
|
91
92
|
* props, else its logical id in kebab case.
|
|
92
93
|
*/
|
|
93
94
|
export function cnrmName(logical, entity) {
|
|
94
|
-
|
|
95
|
+
return cnrmNameOf(logical, isResourceDeclarable(entity) ? entity.props : undefined);
|
|
96
|
+
}
|
|
97
|
+
/** {@link cnrmName} from a resource's props, for callers holding props rather than the declarable. */
|
|
98
|
+
export function cnrmNameOf(logical, props) {
|
|
95
99
|
const declared = props?.metadata?.name;
|
|
96
100
|
return typeof declared === "string" && declared.length > 0 ? declared : kebabName(logical);
|
|
97
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Declared props with each reference to another resource replaced as the
|
|
104
|
+
* serializer writes it (#4251): an attribute reference (`neg.name`) becomes the
|
|
105
|
+
* target's metadata.name, and a resource placed whole becomes `{ name }`.
|
|
106
|
+
* `logicalOf` maps a target's props object to its logical id, for a reference
|
|
107
|
+
* that does not carry one. Other values, pseudo-parameters included, are kept.
|
|
108
|
+
*/
|
|
109
|
+
export function resolveCnrmRefs(value, logicalOf) {
|
|
110
|
+
if (value instanceof AttrRef) {
|
|
111
|
+
const parent = value.parent.deref();
|
|
112
|
+
const props = parent && isResourceDeclarable(parent) ? parent.props : undefined;
|
|
113
|
+
const logical = value.getLogicalName() ?? logicalOf(props);
|
|
114
|
+
return logical === undefined ? value : cnrmNameOf(logical, props);
|
|
115
|
+
}
|
|
116
|
+
if (Array.isArray(value))
|
|
117
|
+
return value.map((v) => resolveCnrmRefs(v, logicalOf));
|
|
118
|
+
if (value === null || typeof value !== "object")
|
|
119
|
+
return value;
|
|
120
|
+
if (isDeclarable(value)) {
|
|
121
|
+
const props = isResourceDeclarable(value) ? value.props : undefined;
|
|
122
|
+
const logical = logicalOf(props);
|
|
123
|
+
return logical === undefined ? value : { name: cnrmNameOf(logical, props) };
|
|
124
|
+
}
|
|
125
|
+
if (INTRINSIC_MARKER in value)
|
|
126
|
+
return value;
|
|
127
|
+
const out = {};
|
|
128
|
+
for (const [k, v] of Object.entries(value))
|
|
129
|
+
out[k] = resolveCnrmRefs(v, logicalOf);
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
98
132
|
function kebabName(logical) {
|
|
99
133
|
return logical.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
100
134
|
}
|
|
@@ -133,7 +167,11 @@ export const gcpSerializer = {
|
|
|
133
167
|
let defaultLabelEntries = context?.ownership
|
|
134
168
|
? { ...ownershipEntries(LABEL_OWNERSHIP_KEYS, context.ownership) }
|
|
135
169
|
: {};
|
|
136
|
-
|
|
170
|
+
// #4252: a member's project, once, in chant.config's `gcp.project`. It
|
|
171
|
+
// resolves GCP.ProjectId and is written as each resource's project-id
|
|
172
|
+
// annotation, which apply and the readers take ahead of the env vars.
|
|
173
|
+
const configProject = gcpConfigProject(context?.config);
|
|
174
|
+
let defaultAnnotationEntries = configProject ? { [PROJECT_ID_ANNOTATION]: configProject } : {};
|
|
137
175
|
for (const [, entity] of entities) {
|
|
138
176
|
if (isDefaultLabels(entity)) {
|
|
139
177
|
defaultLabelEntries = { ...defaultLabelEntries, ...entity.labels };
|
|
@@ -153,7 +191,7 @@ export const gcpSerializer = {
|
|
|
153
191
|
const gvk = resolveGVK(entityType);
|
|
154
192
|
if (!gvk)
|
|
155
193
|
continue;
|
|
156
|
-
const props = resolvePseudoParameters(toYAMLValue(isResourceDeclarable(entity) ? entity.props : undefined, entityNames), name, unresolved);
|
|
194
|
+
const props = resolvePseudoParameters(toYAMLValue(isResourceDeclarable(entity) ? entity.props : undefined, entityNames), name, unresolved, configProject);
|
|
157
195
|
if (!props)
|
|
158
196
|
continue;
|
|
159
197
|
// Build the Config Connector manifest structure
|
|
@@ -201,6 +239,12 @@ export const gcpSerializer = {
|
|
|
201
239
|
return warnings.length > 0 ? { primary, warnings } : primary;
|
|
202
240
|
},
|
|
203
241
|
};
|
|
242
|
+
const PROJECT_ID_ANNOTATION = "cnrm.cloud.google.com/project-id";
|
|
243
|
+
/** The `gcp.project` a chant.config declares, if any (#4252). */
|
|
244
|
+
export function gcpConfigProject(config) {
|
|
245
|
+
const project = config?.gcp?.project;
|
|
246
|
+
return typeof project === "string" && project.length > 0 ? project : undefined;
|
|
247
|
+
}
|
|
204
248
|
/**
|
|
205
249
|
* Pseudo-parameter → environment variable mapping.
|
|
206
250
|
* Config Connector annotations must be plain strings, so PseudoParameters
|
|
@@ -218,16 +262,16 @@ const PSEUDO_ENV_MAP = {
|
|
|
218
262
|
* the API as an object. A variable that isn't set is recorded, and the
|
|
219
263
|
* build refuses with every one named.
|
|
220
264
|
*/
|
|
221
|
-
function resolvePseudoParameters(value, entity, unresolved) {
|
|
265
|
+
function resolvePseudoParameters(value, entity, unresolved, project) {
|
|
222
266
|
if (Array.isArray(value))
|
|
223
|
-
return value.map((v) => resolvePseudoParameters(v, entity, unresolved));
|
|
267
|
+
return value.map((v) => resolvePseudoParameters(v, entity, unresolved, project));
|
|
224
268
|
if (value === null || typeof value !== "object")
|
|
225
269
|
return value;
|
|
226
270
|
const obj = value;
|
|
227
271
|
const keys = Object.keys(obj);
|
|
228
272
|
if (keys.length === 1 && keys[0] === "Ref" && typeof obj.Ref === "string" && obj.Ref in PSEUDO_ENV_MAP) {
|
|
229
273
|
const { envVar } = PSEUDO_ENV_MAP[obj.Ref];
|
|
230
|
-
const resolved = process.env[envVar];
|
|
274
|
+
const resolved = (obj.Ref === "GCP::ProjectId" ? project : undefined) ?? process.env[envVar];
|
|
231
275
|
if (resolved)
|
|
232
276
|
return resolved;
|
|
233
277
|
unresolved.push({ entity, ref: obj.Ref, envVar });
|
|
@@ -235,7 +279,7 @@ function resolvePseudoParameters(value, entity, unresolved) {
|
|
|
235
279
|
}
|
|
236
280
|
const out = {};
|
|
237
281
|
for (const [k, v] of Object.entries(obj))
|
|
238
|
-
out[k] = resolvePseudoParameters(v, entity, unresolved);
|
|
282
|
+
out[k] = resolvePseudoParameters(v, entity, unresolved, project);
|
|
239
283
|
return out;
|
|
240
284
|
}
|
|
241
285
|
function unresolvedMessage(unresolved) {
|
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.37",
|
|
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.37"
|
|
80
80
|
}
|
|
81
81
|
}
|
package/src/deep-observe.ts
CHANGED
|
@@ -61,7 +61,7 @@ import type {
|
|
|
61
61
|
} from "@intentius/chant/lexicon";
|
|
62
62
|
import { deepObservation, normalizeDeepProperties } from "@intentius/chant/deep-observation";
|
|
63
63
|
import { hasOwnershipMarker, type ChannelKeys } from "@intentius/chant/ownership";
|
|
64
|
-
import { deriveGVK, manifestProjectAnnotations, resolveReadProject } from "./describe-resources";
|
|
64
|
+
import { deriveGVK, manifestProjectAnnotations, readTargets, resolveReadProject } from "./describe-resources";
|
|
65
65
|
import { getResource, mapperForKind, GcpReadError, isNotFound, type GcpReadClientOptions } from "./api/read-client";
|
|
66
66
|
import { gcpDeepNormalizationHooks } from "./deep-observe-hooks";
|
|
67
67
|
|
|
@@ -387,8 +387,9 @@ export async function observeResourcesDeepGcp(options: GcpDeepObserveOptions): P
|
|
|
387
387
|
|
|
388
388
|
const endpoint = process.env.GCP_ENDPOINT_URL;
|
|
389
389
|
const manifestProjects = manifestProjectAnnotations(options.buildOutput);
|
|
390
|
+
const targets = readTargets(options.entities);
|
|
390
391
|
|
|
391
|
-
const reads = [...options.entities].map(async ([entityName, { entityType
|
|
392
|
+
const reads = [...options.entities].map(async ([entityName, { entityType }]) => {
|
|
392
393
|
const gvk = deriveGVK(entityType);
|
|
393
394
|
if (!gvk || !mapperForKind(gvk.kind)) {
|
|
394
395
|
unobserved[entityName] = {
|
|
@@ -401,16 +402,8 @@ export async function observeResourcesDeepGcp(options: GcpDeepObserveOptions): P
|
|
|
401
402
|
return;
|
|
402
403
|
}
|
|
403
404
|
|
|
404
|
-
const
|
|
405
|
-
const
|
|
406
|
-
if (!name) {
|
|
407
|
-
unobserved[entityName] = {
|
|
408
|
-
type: entityType,
|
|
409
|
-
reason: "read-failed",
|
|
410
|
-
detail: "declared entity has no metadata.name to query by",
|
|
411
|
-
};
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
405
|
+
const { name, props } = targets.get(entityName)!;
|
|
406
|
+
const metadata = props.metadata as { annotations?: Record<string, string> } | undefined;
|
|
414
407
|
|
|
415
408
|
let client: GcpReadClientOptions;
|
|
416
409
|
try {
|
|
@@ -86,10 +86,11 @@ describe("gcp describeResources — direct REST (#1209)", () => {
|
|
|
86
86
|
expect(fetchMock).not.toHaveBeenCalled();
|
|
87
87
|
});
|
|
88
88
|
|
|
89
|
-
test("an entity with no metadata.name
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
expect(
|
|
89
|
+
test("an entity with no metadata.name is read by the name the build gives it, the kebab-cased logical id (#4251)", async () => {
|
|
90
|
+
fetchMock.mockResolvedValue(reply(200, { name: "web-assets" }));
|
|
91
|
+
const out = await read(makeEntities([{ name: "webAssets", entityType: "GCP::Storage::Bucket", props: {} }]));
|
|
92
|
+
expect(out.resources.webAssets).toBeDefined();
|
|
93
|
+
expect(String(fetchMock.mock.calls[0][0])).toContain("/b/web-assets");
|
|
93
94
|
});
|
|
94
95
|
|
|
95
96
|
test("kinds resolve through the serializer's GVK map, not naive recasing", async () => {
|
|
@@ -40,8 +40,8 @@ import type { ObservationResult, ResourceMetadata, UnobservedEntity } from "@int
|
|
|
40
40
|
import { observation } from "@intentius/chant/observation";
|
|
41
41
|
import { hasOwnershipMarker, classifyOwnership, readOwnership, type ChannelKeys } from "@intentius/chant/ownership";
|
|
42
42
|
import { getResource, mapperForKind, GcpReadError, isNotFound, type GcpReadClientOptions } from "./api/read-client";
|
|
43
|
-
import {
|
|
44
|
-
import { resolveGVK } from "./serializer";
|
|
43
|
+
import { NO_PROJECT, parseManifest, projectRefProject } from "./op/activities/gcp-apply";
|
|
44
|
+
import { cnrmNameOf, resolveCnrmRefs, resolveGVK } from "./serializer";
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* chant's ownership marker as GCP labels.
|
|
@@ -119,11 +119,12 @@ export function manifestProjectAnnotations(buildOutput: string | undefined): Map
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
|
-
* The project a read should target: the entity's
|
|
123
|
-
* `
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
122
|
+
* The project a read should target, in apply's order (#4252): the entity's
|
|
123
|
+
* own literal `projectRef.external`, then the built manifest's project for it
|
|
124
|
+
* (its resolved projectRef, or the project-id annotation chant.config's
|
|
125
|
+
* `gcp.project` writes), then the entity's own annotation, then
|
|
126
|
+
* `GCP_PROJECT_ID`, then `GOOGLE_CLOUD_PROJECT`. Throws apply's own error when
|
|
127
|
+
* nothing resolves, so the `no-binding` detail names every knob.
|
|
127
128
|
*/
|
|
128
129
|
export function resolveReadProject(
|
|
129
130
|
kind: string,
|
|
@@ -131,14 +132,39 @@ export function resolveReadProject(
|
|
|
131
132
|
metadata: { annotations?: Record<string, string> } | undefined,
|
|
132
133
|
manifestProjects: Map<string, string>,
|
|
133
134
|
props?: Record<string, unknown>,
|
|
135
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
134
136
|
): string {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
137
|
+
const project =
|
|
138
|
+
projectRefProject(props, {}) ||
|
|
139
|
+
manifestProjects.get(`${kind}/${name}`) ||
|
|
140
|
+
metadata?.annotations?.["cnrm.cloud.google.com/project-id"] ||
|
|
141
|
+
projectRefProject(props, env) ||
|
|
142
|
+
env.GCP_PROJECT_ID ||
|
|
143
|
+
env.GOOGLE_CLOUD_PROJECT;
|
|
144
|
+
if (!project) throw new Error(NO_PROJECT);
|
|
145
|
+
return project;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Each declared entity as a read sees it (#4251): the name the build writes
|
|
150
|
+
* (`metadata.name`, else the kebab-cased logical id) and its props with every
|
|
151
|
+
* reference to another resource resolved as the serializer resolves it, so a
|
|
152
|
+
* mapper building a read URL never meets an attribute reference.
|
|
153
|
+
*/
|
|
154
|
+
export function readTargets(
|
|
155
|
+
entities: Map<string, { entityType: string; props: Record<string, unknown> }>,
|
|
156
|
+
): Map<string, { name: string; props: Record<string, unknown> }> {
|
|
157
|
+
const logicalByProps = new Map<unknown, string>();
|
|
158
|
+
for (const [logical, { props }] of entities) logicalByProps.set(props, logical);
|
|
159
|
+
const logicalOf = (props: unknown) => logicalByProps.get(props);
|
|
160
|
+
const out = new Map<string, { name: string; props: Record<string, unknown> }>();
|
|
161
|
+
for (const [logical, { props }] of entities) {
|
|
162
|
+
out.set(logical, {
|
|
163
|
+
name: cnrmNameOf(logical, props),
|
|
164
|
+
props: resolveCnrmRefs(props, logicalOf) as Record<string, unknown>,
|
|
165
|
+
});
|
|
141
166
|
}
|
|
167
|
+
return out;
|
|
142
168
|
}
|
|
143
169
|
|
|
144
170
|
function pruneUndefined<T extends Record<string, unknown>>(obj: T): Record<string, unknown> {
|
|
@@ -196,8 +222,9 @@ export async function describeResources(options: {
|
|
|
196
222
|
let warnedOwnership = false;
|
|
197
223
|
|
|
198
224
|
const manifestProjects = manifestProjectAnnotations(options.buildOutput);
|
|
225
|
+
const targets = readTargets(options.entities);
|
|
199
226
|
|
|
200
|
-
const reads = [...options.entities].map(async ([entityName, { entityType
|
|
227
|
+
const reads = [...options.entities].map(async ([entityName, { entityType }]) => {
|
|
201
228
|
const gvk = deriveGVK(entityType);
|
|
202
229
|
if (!gvk) {
|
|
203
230
|
unobserved[entityName] = {
|
|
@@ -218,16 +245,8 @@ export async function describeResources(options: {
|
|
|
218
245
|
return;
|
|
219
246
|
}
|
|
220
247
|
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
if (!name) {
|
|
224
|
-
unobserved[entityName] = {
|
|
225
|
-
type: entityType,
|
|
226
|
-
reason: "read-failed",
|
|
227
|
-
detail: "declared entity has no metadata.name to query by",
|
|
228
|
-
};
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
248
|
+
const { name, props } = targets.get(entityName)!;
|
|
249
|
+
const metadata = props.metadata as { annotations?: Record<string, string> } | undefined;
|
|
231
250
|
|
|
232
251
|
let client: GcpReadClientOptions;
|
|
233
252
|
try {
|
package/src/health.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import type { HealthEntry, HealthReadOptions, HealthReadResult, UnobservedEntity } from "@intentius/chant/lexicon";
|
|
13
13
|
import { getResource, GcpReadError, isNotFound, type GcpReadHttp } from "./api/read-client";
|
|
14
|
-
import { deriveGVK, manifestProjectAnnotations, resolveReadProject } from "./describe-resources";
|
|
14
|
+
import { deriveGVK, manifestProjectAnnotations, readTargets, resolveReadProject } from "./describe-resources";
|
|
15
15
|
|
|
16
16
|
export const GCP_HEALTH_TYPES = ["GCP::Run::Service"] as const;
|
|
17
17
|
|
|
@@ -75,18 +75,15 @@ export async function observeHealthGcp(options: GcpHealthOptions): Promise<Healt
|
|
|
75
75
|
const entities: Record<string, HealthEntry> = {};
|
|
76
76
|
const unobserved: Record<string, UnobservedEntity> = {};
|
|
77
77
|
const absent: string[] = [];
|
|
78
|
+
const targets = readTargets(options.entities);
|
|
78
79
|
|
|
79
80
|
await Promise.all(
|
|
80
|
-
[...options.entities].map(async ([entityName, { entityType
|
|
81
|
+
[...options.entities].map(async ([entityName, { entityType }]) => {
|
|
81
82
|
const gvk = deriveGVK(entityType);
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
if (!gvk
|
|
85
|
-
unobserved[entityName] = {
|
|
86
|
-
type: entityType,
|
|
87
|
-
reason: "read-failed",
|
|
88
|
-
detail: gvk ? "declared entity has no metadata.name to query by" : `cannot derive a GCP kind from ${entityType}`,
|
|
89
|
-
};
|
|
83
|
+
const { name, props } = targets.get(entityName)!;
|
|
84
|
+
const metadata = props.metadata as { annotations?: Record<string, string> } | undefined;
|
|
85
|
+
if (!gvk) {
|
|
86
|
+
unobserved[entityName] = { type: entityType, reason: "read-failed", detail: `cannot derive a GCP kind from ${entityType}` };
|
|
90
87
|
return;
|
|
91
88
|
}
|
|
92
89
|
let project: string;
|
|
@@ -296,9 +296,16 @@ describe("Cloud Run mapper + LRO (#706)", () => {
|
|
|
296
296
|
});
|
|
297
297
|
|
|
298
298
|
describe("resolveGcpProject (#711)", () => {
|
|
299
|
-
test("
|
|
300
|
-
expect(resolveGcpProject(BUCKET, { GOOGLE_CLOUD_PROJECT: "env-project" } as NodeJS.ProcessEnv))
|
|
301
|
-
.toBe("
|
|
299
|
+
test("the annotation (where chant.config's gcp.project lands) wins over the env vars (#4252)", () => {
|
|
300
|
+
expect(resolveGcpProject(BUCKET, { GOOGLE_CLOUD_PROJECT: "env-project", GCP_PROJECT_ID: "id-project" } as NodeJS.ProcessEnv))
|
|
301
|
+
.toBe("annotated-project");
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test("without an annotation, GCP_PROJECT_ID wins over GOOGLE_CLOUD_PROJECT, and projectRef over both (#4252)", () => {
|
|
305
|
+
const bare = { metadata: { name: "b" } };
|
|
306
|
+
expect(resolveGcpProject(bare, { GOOGLE_CLOUD_PROJECT: "g", GCP_PROJECT_ID: "p" } as NodeJS.ProcessEnv)).toBe("p");
|
|
307
|
+
expect(resolveGcpProject(bare, { GOOGLE_CLOUD_PROJECT: "g" } as NodeJS.ProcessEnv)).toBe("g");
|
|
308
|
+
expect(resolveGcpProject({ ...bare, spec: { projectRef: { external: "own" } } }, { GCP_PROJECT_ID: "p" } as NodeJS.ProcessEnv)).toBe("own");
|
|
302
309
|
});
|
|
303
310
|
|
|
304
311
|
test("falls back to the CNRM annotation", () => {
|
|
@@ -568,7 +568,6 @@ export function resolveGcpEndpoint(
|
|
|
568
568
|
return args.endpoint || env.GCP_ENDPOINT_URL || undefined;
|
|
569
569
|
}
|
|
570
570
|
|
|
571
|
-
/** Resolve the project: `GOOGLE_CLOUD_PROJECT` env, else the CNRM project-id annotation. Pure. */
|
|
572
571
|
/**
|
|
573
572
|
* The project a resource names in `spec.projectRef.external` (#4201): a
|
|
574
573
|
* project id or `projects/<id>`. A `GCP.ProjectId` pseudo-parameter there
|
|
@@ -589,19 +588,25 @@ export function projectRefProject(spec: Record<string, unknown> | undefined, env
|
|
|
589
588
|
return external.startsWith("projects/") ? external.slice("projects/".length).split("/")[0] : external;
|
|
590
589
|
}
|
|
591
590
|
|
|
591
|
+
/**
|
|
592
|
+
* The project a resource is applied in (#4252): its own `spec.projectRef.external`,
|
|
593
|
+
* else its project-id annotation (where chant.config's `gcp.project` is
|
|
594
|
+
* written at build), else `GCP_PROJECT_ID`, else `GOOGLE_CLOUD_PROJECT`. Pure.
|
|
595
|
+
*/
|
|
592
596
|
export function resolveGcpProject(resource: GcpResource, env: NodeJS.ProcessEnv = process.env): string {
|
|
593
597
|
const project =
|
|
594
598
|
projectRefProject(resource.spec, env) ||
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
`no GCP project — set spec.projectRef.external, GOOGLE_CLOUD_PROJECT or the ${PROJECT_ID_ANNOTATION} annotation`,
|
|
600
|
-
);
|
|
601
|
-
}
|
|
599
|
+
resource.metadata?.annotations?.[PROJECT_ID_ANNOTATION] ||
|
|
600
|
+
env.GCP_PROJECT_ID ||
|
|
601
|
+
env.GOOGLE_CLOUD_PROJECT;
|
|
602
|
+
if (!project) throw new Error(NO_PROJECT);
|
|
602
603
|
return project;
|
|
603
604
|
}
|
|
604
605
|
|
|
606
|
+
/** The error every stage gives when no project resolves. */
|
|
607
|
+
export const NO_PROJECT =
|
|
608
|
+
`no GCP project — set gcp.project in chant.config, GCP_PROJECT_ID or GOOGLE_CLOUD_PROJECT, or the resource's spec.projectRef.external`;
|
|
609
|
+
|
|
605
610
|
/** Injectable HTTP client — mirrors argo's injectable fetcher so tests avoid the network. */
|
|
606
611
|
export type GcpHttp = (
|
|
607
612
|
method: string,
|
|
@@ -271,6 +271,8 @@ export const iamPolicyMemberMapper: ResourceMapper = {
|
|
|
271
271
|
status: 200,
|
|
272
272
|
// `resourceRef` is the declared one: the policy read is that target's, so the binding is on it.
|
|
273
273
|
text: JSON.stringify({
|
|
274
|
+
// The binding has no id of its own; this one names it, so a read reports a physical id (#4251).
|
|
275
|
+
selfLink: `${target.path}#${spec.role}#${spec.member}`,
|
|
274
276
|
member: spec.member,
|
|
275
277
|
role: spec.role,
|
|
276
278
|
resource: target.path,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, test, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdir, writeFile, rm, realpath } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
6
|
+
import { loadChantConfig } from "@intentius/chant/config";
|
|
7
|
+
import { armSandboxConfigEvaluation, resetSandboxConfigEvaluationForTests } from "@intentius/chant/config-sandbox";
|
|
8
|
+
import { gcpSerializer } from "./serializer";
|
|
9
|
+
import { manifestProjectAnnotations, resolveReadProject } from "./describe-resources";
|
|
10
|
+
import { parseManifest, resolveGcpProject } from "./op/activities/gcp-apply";
|
|
11
|
+
import { CloudRunService, StorageBucket } from "./generated";
|
|
12
|
+
import { defaultAnnotations } from "./default-labels";
|
|
13
|
+
import { GCP } from "./pseudo";
|
|
14
|
+
|
|
15
|
+
/** #4252: one GCP project for build, apply and read, set once in chant.config. */
|
|
16
|
+
|
|
17
|
+
const ANNOTATION = "cnrm.cloud.google.com/project-id";
|
|
18
|
+
|
|
19
|
+
function build(entities: Record<string, unknown>, config?: Record<string, unknown>): string {
|
|
20
|
+
const out = gcpSerializer.serialize(new Map(Object.entries(entities)) as Map<string, Declarable>, undefined, config ? { config } : undefined);
|
|
21
|
+
return typeof out === "string" ? out : out.primary;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe("gcp.project in chant.config (#4252)", () => {
|
|
25
|
+
let saved: NodeJS.ProcessEnv;
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
saved = { ...process.env };
|
|
28
|
+
process.env.GCP_PROJECT_ID = "env-id";
|
|
29
|
+
process.env.GOOGLE_CLOUD_PROJECT = "env-google";
|
|
30
|
+
});
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
process.env = saved;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("the build writes it as each resource's project-id annotation and resolves GCP.ProjectId from it", () => {
|
|
36
|
+
const yaml = build(
|
|
37
|
+
{
|
|
38
|
+
assets: new StorageBucket({ metadata: { name: "assets" }, location: "US" } as never),
|
|
39
|
+
web: new CloudRunService({ metadata: { name: "web" }, location: "us-central1", projectRef: { external: GCP.ProjectId } } as never),
|
|
40
|
+
},
|
|
41
|
+
{ gcp: { project: "cfg-project" } },
|
|
42
|
+
);
|
|
43
|
+
const docs = parseManifest(yaml, "test");
|
|
44
|
+
expect(docs.find((d) => d.metadata?.name === "assets")?.metadata?.annotations?.[ANNOTATION]).toBe("cfg-project");
|
|
45
|
+
expect((docs.find((d) => d.metadata?.name === "web")?.spec as { projectRef: unknown }).projectRef).toEqual({ external: "cfg-project" });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("without it the build writes no annotation, as before", () => {
|
|
49
|
+
const yaml = build({ assets: new StorageBucket({ metadata: { name: "assets" }, location: "US" } as never) });
|
|
50
|
+
expect(parseManifest(yaml, "test")[0].metadata?.annotations?.[ANNOTATION]).toBeUndefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("a defaultAnnotations project wins over the config's", () => {
|
|
54
|
+
const yaml = build(
|
|
55
|
+
{
|
|
56
|
+
assets: new StorageBucket({ metadata: { name: "assets" }, location: "US" } as never),
|
|
57
|
+
annotations: defaultAnnotations({ [ANNOTATION]: "declared-project" }),
|
|
58
|
+
},
|
|
59
|
+
{ gcp: { project: "cfg-project" } },
|
|
60
|
+
);
|
|
61
|
+
expect(parseManifest(yaml, "test")[0].metadata?.annotations?.[ANNOTATION]).toBe("declared-project");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("apply and read both take it ahead of GCP_PROJECT_ID and GOOGLE_CLOUD_PROJECT, and a projectRef ahead of it", () => {
|
|
65
|
+
const yaml = build(
|
|
66
|
+
{
|
|
67
|
+
assets: new StorageBucket({ metadata: { name: "assets" }, location: "US" } as never),
|
|
68
|
+
other: new StorageBucket({ metadata: { name: "other" }, location: "US", projectRef: { external: "own-project" } } as never),
|
|
69
|
+
},
|
|
70
|
+
{ gcp: { project: "cfg-project" } },
|
|
71
|
+
);
|
|
72
|
+
const docs = parseManifest(yaml, "test");
|
|
73
|
+
expect(resolveGcpProject(docs.find((d) => d.metadata?.name === "assets")!)).toBe("cfg-project");
|
|
74
|
+
expect(resolveGcpProject(docs.find((d) => d.metadata?.name === "other")!)).toBe("own-project");
|
|
75
|
+
|
|
76
|
+
const manifest = manifestProjectAnnotations(yaml);
|
|
77
|
+
expect(resolveReadProject("StorageBucket", "assets", undefined, manifest, { metadata: { name: "assets" } })).toBe("cfg-project");
|
|
78
|
+
expect(resolveReadProject("StorageBucket", "other", undefined, manifest, { projectRef: { external: "own-project" } })).toBe("own-project");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("with nothing declared, read falls back to GCP_PROJECT_ID, then GOOGLE_CLOUD_PROJECT", () => {
|
|
82
|
+
const none = new Map<string, string>();
|
|
83
|
+
expect(resolveReadProject("StorageBucket", "b", undefined, none, {})).toBe("env-id");
|
|
84
|
+
delete process.env.GCP_PROJECT_ID;
|
|
85
|
+
expect(resolveReadProject("StorageBucket", "b", undefined, none, {})).toBe("env-google");
|
|
86
|
+
delete process.env.GOOGLE_CLOUD_PROJECT;
|
|
87
|
+
expect(() => resolveReadProject("StorageBucket", "b", undefined, none, {})).toThrow(/gcp\.project in chant\.config/);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("gcp.project under --sandbox (#4252)", () => {
|
|
92
|
+
let dir: string;
|
|
93
|
+
beforeEach(async () => {
|
|
94
|
+
const d = join(tmpdir(), `chant-4252-${Date.now()}-${Math.random()}`);
|
|
95
|
+
await mkdir(d, { recursive: true });
|
|
96
|
+
dir = await realpath(d);
|
|
97
|
+
resetSandboxConfigEvaluationForTests();
|
|
98
|
+
});
|
|
99
|
+
afterEach(async () => {
|
|
100
|
+
resetSandboxConfigEvaluationForTests();
|
|
101
|
+
await rm(dir, { recursive: true, force: true });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("the config crosses from the sandboxed child as data, and the build reads it from there, not from process.env", async () => {
|
|
105
|
+
await writeFile(join(dir, "chant.config.ts"), `export default { lexicons: ["gcp"], gcp: { project: "sandboxed-project" } };\n`);
|
|
106
|
+
armSandboxConfigEvaluation();
|
|
107
|
+
const { config } = await loadChantConfig(dir);
|
|
108
|
+
expect((config as { gcp?: { project?: string } }).gcp?.project).toBe("sandboxed-project");
|
|
109
|
+
|
|
110
|
+
const saved = process.env.GCP_PROJECT_ID;
|
|
111
|
+
delete process.env.GCP_PROJECT_ID;
|
|
112
|
+
try {
|
|
113
|
+
const yaml = build({ assets: new StorageBucket({ metadata: { name: "assets" }, location: "US" } as never) }, config as Record<string, unknown>);
|
|
114
|
+
expect(parseManifest(yaml, "test")[0].metadata?.annotations?.[ANNOTATION]).toBe("sandboxed-project");
|
|
115
|
+
} finally {
|
|
116
|
+
if (saved !== undefined) process.env.GCP_PROJECT_ID = saved;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { describeResources } from "./describe-resources";
|
|
3
|
+
import {
|
|
4
|
+
BackendService,
|
|
5
|
+
CloudRunService,
|
|
6
|
+
ComputeAddress,
|
|
7
|
+
ForwardingRule,
|
|
8
|
+
IAMPolicyMember,
|
|
9
|
+
RegionNetworkEndpointGroup,
|
|
10
|
+
Subnetwork,
|
|
11
|
+
TargetHTTPProxy,
|
|
12
|
+
URLMap,
|
|
13
|
+
VPCNetwork,
|
|
14
|
+
} from "./generated";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* #4251: behold's three-cloud gcp member, read back. Every resource here names
|
|
18
|
+
* another by `.name`, an attribute reference the serializer resolves to the
|
|
19
|
+
* target's metadata.name (#4245). The read path has to resolve it the same way
|
|
20
|
+
* before a mapper builds a URL from the declared props.
|
|
21
|
+
*/
|
|
22
|
+
const fetchMock = vi.fn();
|
|
23
|
+
|
|
24
|
+
function reply(status: number, body: unknown) {
|
|
25
|
+
return { status, text: async () => JSON.stringify(body) };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function estate() {
|
|
29
|
+
const web = new CloudRunService({ metadata: { name: "three-cloud-site" }, location: "us-central1", template: {} } as never);
|
|
30
|
+
const vpc = new VPCNetwork({ metadata: { name: "three-cloud-vpc" }, autoCreateSubnetworks: false } as never);
|
|
31
|
+
const subnet = new Subnetwork({
|
|
32
|
+
metadata: { name: "three-cloud-subnet" },
|
|
33
|
+
region: "us-central1",
|
|
34
|
+
ipCidrRange: "10.8.0.0/24",
|
|
35
|
+
networkRef: { name: vpc.name },
|
|
36
|
+
} as never);
|
|
37
|
+
(web as unknown as { props: Record<string, unknown> }).props.template = {
|
|
38
|
+
containers: [{ image: "gcr.io/p/site:1" }],
|
|
39
|
+
vpcAccess: { egress: "ALL_TRAFFIC", networkInterfaces: [{ networkRef: { name: vpc.name }, subnetworkRef: { name: subnet.name } }] },
|
|
40
|
+
};
|
|
41
|
+
const neg = new RegionNetworkEndpointGroup({
|
|
42
|
+
metadata: { name: "three-cloud-neg" },
|
|
43
|
+
region: "us-central1",
|
|
44
|
+
networkEndpointType: "SERVERLESS",
|
|
45
|
+
cloudRun: { serviceRef: { name: web.name } },
|
|
46
|
+
} as never);
|
|
47
|
+
const backend = new BackendService({
|
|
48
|
+
metadata: { name: "three-cloud-backend" },
|
|
49
|
+
location: "global",
|
|
50
|
+
loadBalancingScheme: "EXTERNAL_MANAGED",
|
|
51
|
+
backend: [{ group: { networkEndpointGroupRef: { name: neg.name } } }],
|
|
52
|
+
} as never);
|
|
53
|
+
const urlMap = new URLMap({
|
|
54
|
+
metadata: { name: "three-cloud-urlmap" },
|
|
55
|
+
location: "global",
|
|
56
|
+
defaultService: { backendServiceRef: { name: backend.name } },
|
|
57
|
+
} as never);
|
|
58
|
+
const proxy = new TargetHTTPProxy({ metadata: { name: "three-cloud-proxy" }, location: "global", urlMapRef: { name: urlMap.name } } as never);
|
|
59
|
+
const address = new ComputeAddress({ metadata: { name: "three-cloud-ip" }, location: "global" } as never);
|
|
60
|
+
const rule = new ForwardingRule({
|
|
61
|
+
metadata: { name: "three-cloud-rule" },
|
|
62
|
+
location: "global",
|
|
63
|
+
loadBalancingScheme: "EXTERNAL_MANAGED",
|
|
64
|
+
portRange: "80",
|
|
65
|
+
target: { targetHTTPProxyRef: { name: proxy.name } },
|
|
66
|
+
ipAddress: { addressRef: { name: address.name } },
|
|
67
|
+
} as never);
|
|
68
|
+
const publicIam = new IAMPolicyMember({
|
|
69
|
+
metadata: { name: "three-cloud-site-public" },
|
|
70
|
+
member: "allUsers",
|
|
71
|
+
role: "roles/run.invoker",
|
|
72
|
+
resourceRef: { apiVersion: "run.cnrm.cloud.google.com/v1beta1", kind: "RunService", name: "three-cloud-site" },
|
|
73
|
+
} as never);
|
|
74
|
+
const all = { web, vpc, subnet, neg, backend, urlMap, proxy, address, rule, publicIam };
|
|
75
|
+
return new Map(
|
|
76
|
+
Object.entries(all).map(([logical, e]) => {
|
|
77
|
+
const entity = e as unknown as { entityType: string; props: Record<string, unknown> };
|
|
78
|
+
return [logical, { entityType: entity.entityType, props: entity.props }];
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
describe("gcp live read resolves .name references as the build does (#4251)", () => {
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
fetchMock.mockReset();
|
|
86
|
+
vi.stubGlobal("fetch", fetchMock);
|
|
87
|
+
process.env.GCP_ENDPOINT_URL = "http://localhost:4588";
|
|
88
|
+
process.env.GOOGLE_CLOUD_PROJECT = "three-cloud";
|
|
89
|
+
fetchMock.mockImplementation(async (url: string) => {
|
|
90
|
+
if (url.includes(":getIamPolicy")) {
|
|
91
|
+
return reply(200, { etag: "e1", bindings: [{ role: "roles/run.invoker", members: ["allUsers"] }] });
|
|
92
|
+
}
|
|
93
|
+
return reply(200, { id: url, name: url.split("/").pop() });
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
afterEach(() => {
|
|
98
|
+
vi.unstubAllGlobals();
|
|
99
|
+
delete process.env.GCP_ENDPOINT_URL;
|
|
100
|
+
delete process.env.GOOGLE_CLOUD_PROJECT;
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("every resource reads, by the names the build writes, and none fails on a reference", async () => {
|
|
104
|
+
const entities = estate();
|
|
105
|
+
const out = await describeResources({ environment: "local", buildOutput: "", entityNames: [...entities.keys()], entities });
|
|
106
|
+
|
|
107
|
+
expect(out.unobserved ?? {}).toEqual({});
|
|
108
|
+
for (const logical of entities.keys()) {
|
|
109
|
+
expect(out.resources[logical]?.physicalId, logical).toBeTruthy();
|
|
110
|
+
}
|
|
111
|
+
const urls = fetchMock.mock.calls.map((c) => String(c[0]));
|
|
112
|
+
for (const name of ["three-cloud-neg", "three-cloud-backend", "three-cloud-urlmap", "three-cloud-proxy", "three-cloud-rule", "three-cloud-subnet", "three-cloud-site"]) {
|
|
113
|
+
expect(urls.some((u) => u.includes(name)), name).toBe(true);
|
|
114
|
+
}
|
|
115
|
+
expect(urls.every((u) => !u.includes("[object"))).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("the public IAMPolicyMember reads as the binding in the service's policy, with a physical id", async () => {
|
|
119
|
+
const entities = estate();
|
|
120
|
+
const out = await describeResources({ environment: "local", buildOutput: "", entityNames: [...entities.keys()], entities });
|
|
121
|
+
expect(out.resources.publicIam?.physicalId).toBe(
|
|
122
|
+
"projects/three-cloud/locations/us-central1/services/three-cloud-site#roles/run.invoker#allUsers",
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("a binding missing from the policy reads as absent, not observed", async () => {
|
|
127
|
+
fetchMock.mockImplementation(async (url: string) =>
|
|
128
|
+
url.includes(":getIamPolicy") ? reply(200, { etag: "e1", bindings: [] }) : reply(200, { id: url }),
|
|
129
|
+
);
|
|
130
|
+
const entities = estate();
|
|
131
|
+
const out = await describeResources({ environment: "local", buildOutput: "", entityNames: [...entities.keys()], entities });
|
|
132
|
+
expect(out.resources.publicIam).toBeUndefined();
|
|
133
|
+
expect(out.unobserved?.publicIam).toBeUndefined();
|
|
134
|
+
});
|
|
135
|
+
});
|
package/src/serializer.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
import { createRequire } from "module";
|
|
9
9
|
import { moduleSourceUrl } from "@intentius/chant/module-source";
|
|
10
10
|
import type { Declarable } from "@intentius/chant/declarable";
|
|
11
|
-
import { isPropertyDeclarable, isResourceDeclarable } from "@intentius/chant/declarable";
|
|
11
|
+
import { isDeclarable, isPropertyDeclarable, isResourceDeclarable } from "@intentius/chant/declarable";
|
|
12
|
+
import { AttrRef } from "@intentius/chant/attrref";
|
|
12
13
|
import type { Serializer, SerializerResult, SerializeContext } from "@intentius/chant/serializer";
|
|
13
14
|
import { ownershipEntries, LABEL_OWNERSHIP_KEYS } from "@intentius/chant/ownership";
|
|
14
15
|
import type { LexiconOutput } from "@intentius/chant/lexicon-output";
|
|
@@ -113,11 +114,42 @@ function gcpVisitor(entityNames: Map<Declarable, string>): SerializerVisitor {
|
|
|
113
114
|
* props, else its logical id in kebab case.
|
|
114
115
|
*/
|
|
115
116
|
export function cnrmName(logical: string, entity: Declarable): string {
|
|
116
|
-
|
|
117
|
-
|
|
117
|
+
return cnrmNameOf(logical, isResourceDeclarable(entity) ? entity.props : undefined);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** {@link cnrmName} from a resource's props, for callers holding props rather than the declarable. */
|
|
121
|
+
export function cnrmNameOf(logical: string, props: unknown): string {
|
|
122
|
+
const declared = (props as { metadata?: { name?: unknown } } | undefined)?.metadata?.name;
|
|
118
123
|
return typeof declared === "string" && declared.length > 0 ? declared : kebabName(logical);
|
|
119
124
|
}
|
|
120
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Declared props with each reference to another resource replaced as the
|
|
128
|
+
* serializer writes it (#4251): an attribute reference (`neg.name`) becomes the
|
|
129
|
+
* target's metadata.name, and a resource placed whole becomes `{ name }`.
|
|
130
|
+
* `logicalOf` maps a target's props object to its logical id, for a reference
|
|
131
|
+
* that does not carry one. Other values, pseudo-parameters included, are kept.
|
|
132
|
+
*/
|
|
133
|
+
export function resolveCnrmRefs(value: unknown, logicalOf: (props: unknown) => string | undefined): unknown {
|
|
134
|
+
if (value instanceof AttrRef) {
|
|
135
|
+
const parent = value.parent.deref() as Declarable | undefined;
|
|
136
|
+
const props = parent && isResourceDeclarable(parent) ? parent.props : undefined;
|
|
137
|
+
const logical = value.getLogicalName() ?? logicalOf(props);
|
|
138
|
+
return logical === undefined ? value : cnrmNameOf(logical, props);
|
|
139
|
+
}
|
|
140
|
+
if (Array.isArray(value)) return value.map((v) => resolveCnrmRefs(v, logicalOf));
|
|
141
|
+
if (value === null || typeof value !== "object") return value;
|
|
142
|
+
if (isDeclarable(value)) {
|
|
143
|
+
const props = isResourceDeclarable(value) ? value.props : undefined;
|
|
144
|
+
const logical = logicalOf(props);
|
|
145
|
+
return logical === undefined ? value : { name: cnrmNameOf(logical, props) };
|
|
146
|
+
}
|
|
147
|
+
if (INTRINSIC_MARKER in (value as object)) return value;
|
|
148
|
+
const out: Record<string, unknown> = {};
|
|
149
|
+
for (const [k, v] of Object.entries(value as Record<string, unknown>)) out[k] = resolveCnrmRefs(v, logicalOf);
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
|
|
121
153
|
function kebabName(logical: string): string {
|
|
122
154
|
return logical.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
123
155
|
}
|
|
@@ -163,7 +195,11 @@ export const gcpSerializer: Serializer = {
|
|
|
163
195
|
let defaultLabelEntries: Record<string, unknown> = context?.ownership
|
|
164
196
|
? { ...ownershipEntries(LABEL_OWNERSHIP_KEYS, context.ownership) }
|
|
165
197
|
: {};
|
|
166
|
-
|
|
198
|
+
// #4252: a member's project, once, in chant.config's `gcp.project`. It
|
|
199
|
+
// resolves GCP.ProjectId and is written as each resource's project-id
|
|
200
|
+
// annotation, which apply and the readers take ahead of the env vars.
|
|
201
|
+
const configProject = gcpConfigProject(context?.config);
|
|
202
|
+
let defaultAnnotationEntries: Record<string, unknown> = configProject ? { [PROJECT_ID_ANNOTATION]: configProject } : {};
|
|
167
203
|
|
|
168
204
|
for (const [, entity] of entities) {
|
|
169
205
|
if (isDefaultLabels(entity)) {
|
|
@@ -189,6 +225,7 @@ export const gcpSerializer: Serializer = {
|
|
|
189
225
|
toYAMLValue(isResourceDeclarable(entity) ? entity.props : undefined, entityNames),
|
|
190
226
|
name,
|
|
191
227
|
unresolved,
|
|
228
|
+
configProject,
|
|
192
229
|
) as Record<string, unknown> | undefined;
|
|
193
230
|
|
|
194
231
|
if (!props) continue;
|
|
@@ -246,6 +283,14 @@ export const gcpSerializer: Serializer = {
|
|
|
246
283
|
},
|
|
247
284
|
};
|
|
248
285
|
|
|
286
|
+
const PROJECT_ID_ANNOTATION = "cnrm.cloud.google.com/project-id";
|
|
287
|
+
|
|
288
|
+
/** The `gcp.project` a chant.config declares, if any (#4252). */
|
|
289
|
+
export function gcpConfigProject(config: Record<string, unknown> | undefined): string | undefined {
|
|
290
|
+
const project = (config?.gcp as { project?: unknown } | undefined)?.project;
|
|
291
|
+
return typeof project === "string" && project.length > 0 ? project : undefined;
|
|
292
|
+
}
|
|
293
|
+
|
|
249
294
|
/**
|
|
250
295
|
* Pseudo-parameter → environment variable mapping.
|
|
251
296
|
* Config Connector annotations must be plain strings, so PseudoParameters
|
|
@@ -270,20 +315,20 @@ interface UnresolvedPseudo {
|
|
|
270
315
|
* the API as an object. A variable that isn't set is recorded, and the
|
|
271
316
|
* build refuses with every one named.
|
|
272
317
|
*/
|
|
273
|
-
function resolvePseudoParameters(value: unknown, entity: string, unresolved: UnresolvedPseudo[]): unknown {
|
|
274
|
-
if (Array.isArray(value)) return value.map((v) => resolvePseudoParameters(v, entity, unresolved));
|
|
318
|
+
function resolvePseudoParameters(value: unknown, entity: string, unresolved: UnresolvedPseudo[], project?: string): unknown {
|
|
319
|
+
if (Array.isArray(value)) return value.map((v) => resolvePseudoParameters(v, entity, unresolved, project));
|
|
275
320
|
if (value === null || typeof value !== "object") return value;
|
|
276
321
|
const obj = value as Record<string, unknown>;
|
|
277
322
|
const keys = Object.keys(obj);
|
|
278
323
|
if (keys.length === 1 && keys[0] === "Ref" && typeof obj.Ref === "string" && obj.Ref in PSEUDO_ENV_MAP) {
|
|
279
324
|
const { envVar } = PSEUDO_ENV_MAP[obj.Ref];
|
|
280
|
-
const resolved = process.env[envVar];
|
|
325
|
+
const resolved = (obj.Ref === "GCP::ProjectId" ? project : undefined) ?? process.env[envVar];
|
|
281
326
|
if (resolved) return resolved;
|
|
282
327
|
unresolved.push({ entity, ref: obj.Ref, envVar });
|
|
283
328
|
return value;
|
|
284
329
|
}
|
|
285
330
|
const out: Record<string, unknown> = {};
|
|
286
|
-
for (const [k, v] of Object.entries(obj)) out[k] = resolvePseudoParameters(v, entity, unresolved);
|
|
331
|
+
for (const [k, v] of Object.entries(obj)) out[k] = resolvePseudoParameters(v, entity, unresolved, project);
|
|
287
332
|
return out;
|
|
288
333
|
}
|
|
289
334
|
|