@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.
@@ -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,CA8F5G"}
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"}
@@ -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 reads = [...options.entities].map(async ([entityName, { entityType, props }]) => {
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 `projectRef.external`,
86
- * `GOOGLE_CLOUD_PROJECT`, the entity's own annotation (via
87
- * `resolveGcpProject`, the applier's rule), then the built
88
- * manifest's merged annotation. Throws the applier's own error when nothing
89
- * resolves, so the `no-binding` detail names both knobs.
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>): string;
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;;;;;;GAMG;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,GAC9B,MAAM,CAQR;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,CAwH7B"}
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 { resolveGcpProject, parseManifest, projectRefProject } from "./op/activities/gcp-apply.js";
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 `projectRef.external`,
103
- * `GOOGLE_CLOUD_PROJECT`, the entity's own annotation (via
104
- * `resolveGcpProject`, the applier's rule), then the built
105
- * manifest's merged annotation. Throws the applier's own error when nothing
106
- * resolves, so the `no-binding` detail names both knobs.
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
- try {
110
- return resolveGcpProject({ kind, metadata, ...(props ? { spec: props } : {}) });
111
- }
112
- catch (err) {
113
- const fromManifest = manifestProjects.get(`${kind}/${name}`);
114
- if (fromManifest)
115
- return fromManifest;
116
- throw err;
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 reads = [...options.entities].map(async ([entityName, { entityType, props }]) => {
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 = {
@@ -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,CAoD3F"}
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
- await Promise.all([...options.entities].map(async ([entityName, { entityType, props }]) => {
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
- const name = metadata?.name;
53
- if (!gvk || !name) {
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;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "9c67ab15a45ae8ce5c5ac3a6e3a83295bd28e78a4acf71b0d00d74040f5d27e0",
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": "f841125ccfbe364d6981cf4a1f444c9edeaa213e3b5d562dce7b12b341d82daf"
43
+ "composite": "12781f2fbe08546cbfed0496ef97a0cfc6f301c5576bfafad2cbd4bf3cac39f3"
44
44
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gcp",
3
- "version": "0.124.36",
3
+ "version": "0.124.37",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GCP",
6
6
  "intrinsics": [],
@@ -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
- env.GOOGLE_CLOUD_PROJECT ||
453
- resource.metadata?.annotations?.[PROJECT_ID_ANNOTATION];
454
- if (!project) {
455
- throw new Error(`no GCP project — set spec.projectRef.external, GOOGLE_CLOUD_PROJECT or the ${PROJECT_ID_ANNOTATION} annotation`);
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,cA8CnC,CAAC"}
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,
@@ -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
@@ -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;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"}
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"}
@@ -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
- const props = isResourceDeclarable(entity) ? entity.props : undefined;
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
- let defaultAnnotationEntries = {};
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.36",
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.36"
79
+ "@intentius/chant": "^0.124.37"
80
80
  }
81
81
  }
@@ -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, props }]) => {
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 metadata = props.metadata as { name?: string; annotations?: Record<string, string> } | undefined;
405
- const name = metadata?.name;
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 has nothing to query by", async () => {
90
- const out = await read(makeEntities([{ name: "e", entityType: "GCP::Storage::Bucket", props: {} }]));
91
- expect(out.unobserved?.e?.reason).toBe("read-failed");
92
- expect(fetchMock).not.toHaveBeenCalled();
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 { resolveGcpProject, parseManifest, projectRefProject } from "./op/activities/gcp-apply";
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 `projectRef.external`,
123
- * `GOOGLE_CLOUD_PROJECT`, the entity's own annotation (via
124
- * `resolveGcpProject`, the applier's rule), then the built
125
- * manifest's merged annotation. Throws the applier's own error when nothing
126
- * resolves, so the `no-binding` detail names both knobs.
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
- try {
136
- return resolveGcpProject({ kind, metadata, ...(props ? { spec: props } : {}) });
137
- } catch (err) {
138
- const fromManifest = manifestProjects.get(`${kind}/${name}`);
139
- if (fromManifest) return fromManifest;
140
- throw err;
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, props }]) => {
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 metadata = props.metadata as { name?: string; annotations?: Record<string, string> } | undefined;
222
- const name = metadata?.name;
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, props }]) => {
81
+ [...options.entities].map(async ([entityName, { entityType }]) => {
81
82
  const gvk = deriveGVK(entityType);
82
- const metadata = props.metadata as { name?: string; annotations?: Record<string, string> } | undefined;
83
- const name = metadata?.name;
84
- if (!gvk || !name) {
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("env wins over the annotation", () => {
300
- expect(resolveGcpProject(BUCKET, { GOOGLE_CLOUD_PROJECT: "env-project" } as NodeJS.ProcessEnv))
301
- .toBe("env-project");
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
- env.GOOGLE_CLOUD_PROJECT ||
596
- resource.metadata?.annotations?.[PROJECT_ID_ANNOTATION];
597
- if (!project) {
598
- throw new Error(
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
- const props = isResourceDeclarable(entity) ? (entity.props as { metadata?: { name?: unknown } } | undefined) : undefined;
117
- const declared = props?.metadata?.name;
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
- let defaultAnnotationEntries: Record<string, unknown> = {};
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