@intentius/chant-lexicon-gcp 0.0.22 → 0.1.0

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,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "xxhash64",
3
3
  "artifacts": {
4
- "manifest.json": "69cfd117e80c85ba",
4
+ "manifest.json": "97b97a197e65cc96",
5
5
  "meta.json": "4b53e87923a434ef",
6
6
  "types/index.d.ts": "c7cc45a739cefe9d",
7
7
  "rules/hardcoded-region.ts": "d230565c5cc6b600",
@@ -24,7 +24,7 @@
24
24
  "rules/wgc204.ts": "bf9cb93ace50c4b0",
25
25
  "rules/wgc401.ts": "35fcebf42c7379c7",
26
26
  "rules/schema-registry.ts": "6327d2dc2fed726e",
27
- "rules/wgc101.ts": "e8d7866cfbf7cc3d",
27
+ "rules/wgc101.ts": "170fc08848092171",
28
28
  "rules/wgc104.ts": "56b3c40af42e9302",
29
29
  "rules/wgc112.ts": "34b22234dfca1a17",
30
30
  "rules/wgc107.ts": "ae6dcd327490da06",
@@ -32,10 +32,10 @@
32
32
  "rules/wgc402.ts": "7f054e29d146290f",
33
33
  "rules/wgc106.ts": "180629dfc4f2934",
34
34
  "rules/wgc113.ts": "677724b28a9dbd5c",
35
- "skills/chant-gcp.md": "a89b47dcffdbaaef",
36
- "skills/chant-gcp-security.md": "e93c103ba16b6d87",
37
- "skills/chant-gcp-patterns.md": "ab5dea252128c7d2",
38
- "skills/chant-gke.md": "15b6fd248379f66"
35
+ "skills/chant-gcp.md": "13a559014514fdb0",
36
+ "skills/chant-gcp-security.md": "9ed168e1fb54d31b",
37
+ "skills/chant-gcp-patterns.md": "6cd0a4a78933323c",
38
+ "skills/chant-gcp-gke.md": "f185f6c0de514ba0"
39
39
  },
40
- "composite": "6d2e8a6c8c8c8cf2"
40
+ "composite": "9e2d76181eb1fdac"
41
41
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gcp",
3
- "version": "0.0.22",
3
+ "version": "0.1.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GCP",
6
6
  "intrinsics": [],
@@ -30,7 +30,7 @@ export const wgc101: PostSynthCheck = {
30
30
  diagnostics.push({
31
31
  checkId: "WGC101",
32
32
  severity: "warning",
33
- message: `StorageBucket "${name}" has no encryption configuration — consider adding spec.encryption.defaultKmsKeyName`,
33
+ message: `StorageBucket "${name}" has no encryption configuration — consider adding spec.encryption.kmsKeyRef`,
34
34
  entity: name,
35
35
  lexicon: "gcp",
36
36
  });
@@ -1,5 +1,5 @@
1
1
  ---
2
- skill: chant-gke
2
+ skill: chant-gcp-gke
3
3
  description: End-to-end GKE workflow bridging GCP infrastructure and Kubernetes workloads
4
4
  user-invocable: true
5
5
  ---
@@ -1,6 +1,7 @@
1
1
  ---
2
- source: chant-lexicon
3
- lexicon: gcp
2
+ skill: chant-gcp-patterns
3
+ description: Advanced GCP Config Connector patterns
4
+ user-invocable: true
4
5
  ---
5
6
 
6
7
  # Advanced GCP Config Connector Patterns with Chant
@@ -1,6 +1,7 @@
1
1
  ---
2
- source: chant-lexicon
3
- lexicon: gcp
2
+ skill: chant-gcp-security
3
+ description: GCP security best practices for infrastructure
4
+ user-invocable: true
4
5
  ---
5
6
 
6
7
  # GCP Security Best Practices for Chant
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  skill: chant-gcp
3
3
  description: Build, validate, and deploy GCP Config Connector manifests from a chant project
4
- source: chant-lexicon
5
4
  user-invocable: true
6
5
  ---
7
6
 
@@ -18,47 +17,117 @@ The source of truth for infrastructure is the TypeScript in `src/`. The generate
18
17
 
19
18
  ## Prerequisites
20
19
 
21
- 1. A GKE cluster with Config Connector installed
22
- 2. A ConfigConnectorContext resource per namespace
23
- 3. A GCP Service Account with appropriate IAM roles
20
+ 1. A GKE cluster with Config Connector installed and the controller running
21
+ 2. A ConfigConnectorContext resource per namespace binding an IAM service account
22
+ 3. A GCP Service Account with appropriate IAM roles for the resources you declare
23
+ 4. `kubectl` configured with credentials for the target cluster
24
+ 5. `chant` installed (`npm install --save-dev @intentius/chant @intentius/chant-lexicon-gcp`)
25
+ 6. Workload Identity enabled on the GKE node pool (recommended over key-based auth)
24
26
 
25
- ## Build and validate
27
+ Verify Config Connector is healthy before your first deploy:
26
28
 
27
- ### Build manifests
29
+ ```bash
30
+ kubectl get configconnectorcontexts -A
31
+ kubectl get pods -n cnrm-system
32
+ ```
33
+
34
+ Both should show `Running` / `Healthy`. If the controller pod is crashlooping, no resources will reconcile.
35
+
36
+ ## Build and validate workflow
37
+
38
+ ### 1. Lint the source
39
+
40
+ ```bash
41
+ chant lint src/
42
+ ```
43
+
44
+ Runs pre-synth static analysis on your TypeScript. Fix all warnings before building.
45
+
46
+ ### 2. Build manifests
28
47
 
29
48
  ```bash
30
49
  chant build src/ --output manifests.yaml
31
50
  ```
32
51
 
33
- ### Lint the source
52
+ Synthesizes TypeScript into Config Connector YAML and runs post-synth checks on the output.
53
+
54
+ ### 3. Server-side dry run
34
55
 
35
56
  ```bash
36
- chant lint src/
57
+ kubectl apply -f manifests.yaml --dry-run=server
37
58
  ```
38
59
 
60
+ Validates the manifests against the Kubernetes API server schema. Catches CRD version mismatches, unknown fields, and missing namespaces.
61
+
39
62
  ### What each step catches
40
63
 
41
- | Step | Catches | When to run |
42
- |------|---------|-------------|
43
- | `chant lint` | Hardcoded project IDs (WGC001), regions (WGC002), public IAM (WGC003) | Every edit |
44
- | `chant build` | Post-synth: missing encryption (WGC101), public IAM in output (WGC102), missing project annotation (WGC103) | Before apply |
64
+ | Step | Rule IDs | Catches | When to run |
65
+ |------|----------|---------|-------------|
66
+ | `chant lint` | WGC001 | Hardcoded project IDs | Every edit |
67
+ | `chant lint` | WGC002 | Hardcoded regions | Every edit |
68
+ | `chant lint` | WGC003 | Public IAM bindings (allUsers/allAuthenticatedUsers) | Every edit |
69
+ | `chant build` (post-synth) | WGC101 | Missing encryption (CMEK or Google-managed) | Before apply |
70
+ | `chant build` (post-synth) | WGC102 | Public IAM bindings in synthesized output | Before apply |
71
+ | `chant build` (post-synth) | WGC103 | Missing project annotation on resource | Before apply |
72
+ | `chant build` (post-synth) | WGC104 | Missing uniform bucket-level access on GCS | Before apply |
73
+ | `chant build` (post-synth) | WGC105 | Public Cloud SQL (ipConfiguration.ipv4Enabled) | Before apply |
74
+ | `chant build` (post-synth) | WGC106 | Missing deletion policy annotation | Before apply |
75
+ | `chant build` (post-synth) | WGC107 | Missing versioning on GCS buckets | Before apply |
76
+ | `chant build` (post-synth) | WGC108 | Missing backup configuration on Cloud SQL | Before apply |
77
+ | `chant build` (post-synth) | WGC109 | Open firewall rules (0.0.0.0/0) | Before apply |
78
+ | `chant build` (post-synth) | WGC110 | Missing key rotation period on KMS keys | Before apply |
79
+ | `chant build` (post-synth) | WGC111 | Dangling resource reference (resourceRef target missing) | Before apply |
80
+ | `chant build` (post-synth) | WGC112 | Missing or invalid apiVersion on CRD | Before apply |
81
+ | `chant build` (post-synth) | WGC113 | Alpha API version (unstable, may break) | Before apply |
82
+ | `chant build` (post-synth) | WGC201 | Missing managed-by label | Before apply |
83
+ | `chant build` (post-synth) | WGC202 | Missing Workload Identity on GKE node pool | Before apply |
84
+ | `chant build` (post-synth) | WGC203 | Overly broad cloud-platform OAuth scope | Before apply |
85
+ | `chant build` (post-synth) | WGC204 | Missing shielded VM config on GKE nodes | Before apply |
86
+ | `chant build` (post-synth) | WGC301 | No audit logging configured on project | Before apply |
87
+ | `chant build` (post-synth) | WGC302 | Service API not enabled for resource type | Before apply |
88
+ | `chant build` (post-synth) | WGC303 | Missing VPC Service Controls perimeter | Before apply |
89
+ | `chant build` (post-synth) | WGC401 | Unknown spec field (typo or wrong CRD version) | Before apply |
90
+ | `chant build` (post-synth) | WGC402 | Missing required field in spec | Before apply |
91
+ | `chant build` (post-synth) | WGC403 | Type/structure mismatch (string vs object, etc.) | Before apply |
92
+ | `kubectl --dry-run=server` | — | CRD not installed, namespace missing, schema violations | Before apply |
45
93
 
46
94
  ## Applying to Kubernetes
47
95
 
48
96
  ```bash
49
- # Build
97
+ # 1. Build
50
98
  chant build src/ --output manifests.yaml
51
99
 
52
- # See what changed since last deploy (compares current build against last snapshot's digest)
100
+ # 2. See what changed since last deploy (compares current build against last snapshot's digest)
53
101
  chant state diff staging gcp
54
102
 
55
- # Dry run
103
+ # 3. Diff against live cluster state
104
+ kubectl diff -f manifests.yaml
105
+
106
+ # 4. Dry run against API server
56
107
  kubectl apply -f manifests.yaml --dry-run=server
57
108
 
58
- # Apply
109
+ # 5. Apply
59
110
  kubectl apply -f manifests.yaml
60
111
  ```
61
112
 
113
+ ## Diffing and change preview
114
+
115
+ Use `kubectl diff` to see exactly what will change before applying:
116
+
117
+ ```bash
118
+ kubectl diff -f manifests.yaml
119
+ ```
120
+
121
+ This compares your local manifests against the live cluster state. Exit code 0 means no changes; exit code 1 means differences exist. The output is a unified diff showing additions, removals, and modifications.
122
+
123
+ For chant-level diffing (comparing against the last snapshot rather than live state):
124
+
125
+ ```bash
126
+ chant state diff staging gcp
127
+ ```
128
+
129
+ This is faster (no cluster access needed) and works offline, but only reflects the last time you captured a snapshot.
130
+
62
131
  ## Resource reference patterns
63
132
 
64
133
  Config Connector resources reference each other using `resourceRef`:
@@ -73,6 +142,19 @@ resourceRef:
73
142
  external: projects/my-project/global/networks/my-network
74
143
  ```
75
144
 
145
+ Use in-namespace `name` references when both resources are in the same chant project and namespace. Use `external` references for pre-existing resources or cross-project dependencies.
146
+
147
+ ### Common reference patterns
148
+
149
+ | Source resource | Target resource | Reference field |
150
+ |----------------|----------------|-----------------|
151
+ | ComputeSubnetwork | ComputeNetwork | `spec.networkRef` |
152
+ | ComputeFirewall | ComputeNetwork | `spec.networkRef` |
153
+ | SQLInstance | ComputeNetwork | `spec.settings.ipConfiguration.privateNetworkRef` |
154
+ | IAMPolicyMember | Any | `spec.resourceRef` |
155
+ | DNSRecordSet | DNSManagedZone | `spec.managedZoneRef` |
156
+ | ContainerNodePool | ContainerCluster | `spec.clusterRef` |
157
+
76
158
  ## Project binding
77
159
 
78
160
  Bind resources to a GCP project via annotations:
@@ -91,21 +173,271 @@ export const annotations = defaultAnnotations({
91
173
  });
92
174
  ```
93
175
 
94
- ## Troubleshooting
176
+ ### Folder and org binding
177
+
178
+ For organization-level resources, use the equivalent annotations:
179
+
180
+ ```yaml
181
+ # Folder-scoped
182
+ cnrm.cloud.google.com/folder-id: "123456789"
183
+
184
+ # Organization-scoped
185
+ cnrm.cloud.google.com/organization-id: "987654321"
186
+ ```
187
+
188
+ ## Composites reference
189
+
190
+ Composites group related Config Connector resources with secure defaults. Each returns an object containing the individual resources.
191
+
192
+ | Composite | Creates | Secure defaults |
193
+ |-----------|---------|-----------------|
194
+ | `GkeCluster` | ContainerCluster + ContainerNodePool | Workload Identity, shielded nodes, private cluster, VPC-native |
195
+ | `CloudRunServiceComposite` | RunService + IAMPolicyMember | No allUsers by default, concurrency limits, CPU throttling |
196
+ | `CloudSqlInstance` | SQLInstance + SQLDatabase + SQLUser | Private IP, backups enabled, deletion protection, SSL required |
197
+ | `GcsBucket` | StorageBucket | Uniform access, versioning, encryption, deletion policy |
198
+ | `VpcNetwork` | ComputeNetwork + ComputeSubnetwork(s) + ComputeFirewall(s) | Private Google Access, flow logs, no default 0.0.0.0/0 rule |
199
+ | `PubSubPipeline` | PubSubTopic + PubSubSubscription | Message retention, dead letter topic, ack deadline |
200
+ | `CloudFunctionWithTrigger` | CloudFunction + EventTrigger or HttpsTrigger | VPC connector, ingress settings, service account |
201
+ | `PrivateService` | ComputeGlobalAddress + ServiceNetworkingConnection | RFC 1918 range, automatic peering |
202
+ | `ManagedCertificate` | ComputeManagedSslCertificate | Auto-renewal via Google-managed cert |
203
+ | `SecureProject` | Project + IAMAuditConfig + essential Service resources | Audit logging, required APIs enabled, org policy constraints |
204
+
205
+ ### Example: GKE cluster with secure defaults
206
+
207
+ ```typescript
208
+ import { GkeCluster } from "@intentius/chant-lexicon-gcp";
209
+
210
+ const { cluster, nodePool } = GkeCluster({
211
+ name: "prod-cluster",
212
+ location: "us-central1",
213
+ initialNodeCount: 3,
214
+ minNodeCount: 1,
215
+ maxNodeCount: 5,
216
+ machineType: "e2-standard-4",
217
+ });
218
+ ```
219
+
220
+ ### Example: Cloud SQL with private networking
221
+
222
+ ```typescript
223
+ import { CloudSqlInstance, PrivateService } from "@intentius/chant-lexicon-gcp";
224
+
225
+ const { globalAddress, connection } = PrivateService({
226
+ name: "sql-peering",
227
+ networkName: "default",
228
+ });
229
+
230
+ const { instance, database } = CloudSqlInstance({
231
+ name: "app-db",
232
+ databaseVersion: "POSTGRES_15",
233
+ tier: "db-custom-2-8192",
234
+ privateNetworkRef: { name: "default" },
235
+ });
236
+ ```
237
+
238
+ ## Deploy lifecycle
239
+
240
+ ### 1. Build and validate
241
+
242
+ ```bash
243
+ chant build src/ --output manifests.yaml
244
+ chant lint src/
245
+ kubectl apply -f manifests.yaml --dry-run=server
246
+ ```
247
+
248
+ ### 2. Apply
249
+
250
+ ```bash
251
+ kubectl apply -f manifests.yaml
252
+ ```
253
+
254
+ ### 3. Wait for Ready
255
+
256
+ Config Connector resources reconcile asynchronously. Wait for the `Ready` condition:
257
+
258
+ ```bash
259
+ # Watch all Config Connector resources
260
+ kubectl get gcp -w
261
+
262
+ # Wait for a specific resource
263
+ kubectl wait --for=condition=Ready sqldatabase/app-db --timeout=600s
264
+ ```
265
+
266
+ Most resources reconcile in 30-120 seconds. Cloud SQL instances can take 5-10 minutes. GKE clusters can take 10-20 minutes.
267
+
268
+ ### 4. Verify
269
+
270
+ ```bash
271
+ # Check all resource statuses
272
+ kubectl get gcp
273
+
274
+ # Detailed status for a specific resource
275
+ kubectl describe computeinstance/web-server
276
+
277
+ # Capture a snapshot for future diffing
278
+ chant state snapshot staging gcp
279
+ ```
280
+
281
+ ### 5. Rollback
282
+
283
+ To roll back, revert the TypeScript source, rebuild, and reapply:
284
+
285
+ ```bash
286
+ git checkout HEAD~1 -- src/
287
+ chant build src/ --output manifests.yaml
288
+ kubectl apply -f manifests.yaml
289
+ ```
290
+
291
+ For emergency rollback of a single resource, delete it from the cluster. Config Connector will NOT delete the underlying GCP resource unless the deletion policy annotation is set to `abandon: false`:
292
+
293
+ ```bash
294
+ # Check deletion policy first
295
+ kubectl get computeinstance/web-server -o jsonpath='{.metadata.annotations.cnrm\.cloud\.google\.com/deletion-policy}'
296
+
297
+ # If "abandon" (default), deleting the K8s object leaves the GCP resource intact
298
+ kubectl delete computeinstance/web-server
299
+ ```
300
+
301
+ ## Multi-project patterns
302
+
303
+ ### Namespace-per-project
304
+
305
+ Create a Kubernetes namespace per GCP project, each with its own ConfigConnectorContext:
306
+
307
+ ```yaml
308
+ apiVersion: core.cnrm.cloud.google.com/v1beta1
309
+ kind: ConfigConnectorContext
310
+ metadata:
311
+ name: configconnectorcontext.core.cnrm.cloud.google.com
312
+ namespace: project-a
313
+ spec:
314
+ googleServiceAccount: cnrm-sa@project-a.iam.gserviceaccount.com
315
+ ```
316
+
317
+ Then target a namespace when applying:
318
+
319
+ ```bash
320
+ kubectl apply -f manifests-project-a.yaml -n project-a
321
+ kubectl apply -f manifests-project-b.yaml -n project-b
322
+ ```
323
+
324
+ ### Cross-project references
325
+
326
+ Use `external` refs to reference resources in other projects:
327
+
328
+ ```typescript
329
+ const subnet = new ComputeSubnetwork({
330
+ name: "app-subnet",
331
+ networkRef: {
332
+ external: "projects/shared-vpc-project/global/networks/shared-vpc",
333
+ },
334
+ });
335
+ ```
336
+
337
+ ### Shared VPC pattern
338
+
339
+ In a Shared VPC setup, the host project owns the VPC and the service projects consume subnets:
340
+
341
+ ```typescript
342
+ // In host project namespace
343
+ const { network, subnets } = VpcNetwork({
344
+ name: "shared-vpc",
345
+ subnets: [
346
+ { name: "svc-a-subnet", cidr: "10.0.1.0/24", region: "us-central1" },
347
+ { name: "svc-b-subnet", cidr: "10.0.2.0/24", region: "us-central1" },
348
+ ],
349
+ });
350
+
351
+ // In service project namespace — reference by external
352
+ const instance = new ComputeInstance({
353
+ name: "app-vm",
354
+ subnetworkRef: {
355
+ external: "projects/host-project/regions/us-central1/subnetworks/svc-a-subnet",
356
+ },
357
+ });
358
+ ```
359
+
360
+ ## Troubleshooting decision tree
361
+
362
+ ### Resource stuck in a non-Ready state
363
+
364
+ ```
365
+ Is the resource status "UpToDate"?
366
+ YES -> Resource is reconciled. Done.
367
+ NO -> Check the status condition:
368
+ "UpdateFailed"
369
+ -> kubectl describe <resource> — read the Events section
370
+ -> Common causes:
371
+ - IAM permission denied -> Grant the missing role to the Config Connector SA
372
+ - Quota exceeded -> Request quota increase or reduce resource count
373
+ - Invalid field value -> Fix the TypeScript source and rebuild
374
+ "DependencyNotReady"
375
+ -> The resourceRef target hasn't reconciled yet
376
+ -> Check the referenced resource: kubectl get <target-kind>/<target-name>
377
+ -> If the target doesn't exist, add it to your chant source
378
+ "DeletionFailed"
379
+ -> Cannot delete the GCP resource
380
+ -> Check IAM permissions for deletion
381
+ -> Some resources (Cloud SQL, GKE) have deletion protection; disable it first
382
+ "Updating"
383
+ -> Resource is actively being updated. Wait.
384
+ -> If stuck for >10 min: kubectl describe to check for errors
385
+ ```
386
+
387
+ ### Common error patterns
388
+
389
+ | Error message | Cause | Fix |
390
+ |--------------|-------|-----|
391
+ | `Permission denied on resource` | Config Connector SA lacks IAM role | `gcloud projects add-iam-policy-binding` |
392
+ | `Resource already exists` | Resource was created outside Config Connector | Use `cnrm.cloud.google.com/state-into-spec: absent` to adopt |
393
+ | `Quota exceeded` | Project quota limit reached | Request increase or adjust resource sizing |
394
+ | `Invalid value for field` | Spec value rejected by GCP API | Check field constraints in GCP docs |
395
+ | `The referenced resource does not exist` | Dangling resourceRef | Ensure the referenced resource is defined and in the same namespace or use `external` |
396
+ | `Namespace not found` | Target namespace doesn't exist | `kubectl create namespace <ns>` |
397
+ | `no matches for kind` | Config Connector CRD not installed | Install or update Config Connector |
398
+ | `alpha API version in use` | Using v1alpha1 CRD that may change | Pin to v1beta1 or v1 if available |
399
+
400
+ ### Adopting existing resources
401
+
402
+ To bring a pre-existing GCP resource under Config Connector management:
403
+
404
+ ```yaml
405
+ metadata:
406
+ annotations:
407
+ cnrm.cloud.google.com/state-into-spec: absent
408
+ ```
95
409
 
96
- | Status | Meaning | Fix |
97
- |--------|---------|-----|
98
- | UpToDate | Resource is in sync | None needed |
99
- | UpdateFailed | GCP API error | Check `kubectl describe` events |
100
- | DependencyNotReady | Waiting for referenced resource | Ensure dependency exists |
101
- | DeletionFailed | Cannot delete GCP resource | Check IAM permissions |
410
+ This tells Config Connector to adopt the resource without overwriting its current settings.
102
411
 
103
- ## Quick reference
412
+ ## Quick reference commands
104
413
 
105
414
  | Command | Description |
106
415
  |---------|-------------|
107
- | `chant build src/` | Synthesize manifests |
108
- | `chant lint src/` | Check for anti-patterns |
109
- | `kubectl apply -f manifests.yaml` | Apply to cluster |
416
+ | `chant build src/` | Synthesize Config Connector manifests |
417
+ | `chant build src/ --output manifests.yaml` | Synthesize to a specific file |
418
+ | `chant lint src/` | Check for anti-patterns (pre-synth + post-synth) |
419
+ | `chant state diff staging gcp` | Compare current build against last snapshot |
420
+ | `chant state snapshot staging gcp` | Capture current cluster state |
421
+ | `kubectl apply -f manifests.yaml` | Apply manifests to cluster |
422
+ | `kubectl apply -f manifests.yaml --dry-run=server` | Validate against API server |
423
+ | `kubectl diff -f manifests.yaml` | Preview changes against live state |
110
424
  | `kubectl get gcp` | List all Config Connector resources |
111
- | `kubectl describe <resource>` | Check reconciliation status |
425
+ | `kubectl get gcp -w` | Watch Config Connector resource status |
426
+ | `kubectl describe <kind>/<name>` | Detailed resource status and events |
427
+ | `kubectl wait --for=condition=Ready <kind>/<name> --timeout=300s` | Block until resource is ready |
428
+ | `kubectl delete <kind>/<name>` | Remove from cluster (deletion policy controls GCP resource) |
429
+ | `kubectl get configconnectorcontexts -A` | Check Config Connector health |
430
+ | `kubectl get pods -n cnrm-system` | Check Config Connector controller pods |
431
+ | `kubectl logs -n cnrm-system -l cnrm.cloud.google.com/component=cnrm-controller-manager` | Controller logs |
432
+
433
+ ## Labels and annotations reference
434
+
435
+ | Annotation / Label | Purpose | Example |
436
+ |-------------------|---------|---------|
437
+ | `cnrm.cloud.google.com/project-id` | Bind resource to a GCP project | `my-project-id` |
438
+ | `cnrm.cloud.google.com/folder-id` | Bind to a folder | `123456789` |
439
+ | `cnrm.cloud.google.com/organization-id` | Bind to an org | `987654321` |
440
+ | `cnrm.cloud.google.com/deletion-policy` | Control GCP resource on K8s delete | `abandon` (default) or `delete` |
441
+ | `cnrm.cloud.google.com/state-into-spec` | Adopt existing resources | `absent` |
442
+ | `cnrm.cloud.google.com/force-conflicts` | Allow Config Connector to overwrite | `true` |
443
+ | `managed-by` label | Operational tracking (WGC201) | `chant` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-gcp",
3
- "version": "0.0.22",
3
+ "version": "0.1.0",
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",
@@ -43,11 +43,14 @@
43
43
  "prepack": "bun run generate && bun run bundle && bun run validate"
44
44
  },
45
45
  "dependencies": {
46
- "@intentius/chant": "0.0.22",
47
46
  "fflate": "^0.8.2",
48
47
  "js-yaml": "^4.1.0"
49
48
  },
50
49
  "devDependencies": {
50
+ "@intentius/chant": "0.1.0",
51
51
  "typescript": "^5.9.3"
52
+ },
53
+ "peerDependencies": {
54
+ "@intentius/chant": "^0.1.0"
52
55
  }
53
56
  }
@@ -2,6 +2,13 @@
2
2
  * CloudFunctionWithTrigger composite — CloudFunction + source bucket + optional PubSub/HTTP trigger + invoker IAM.
3
3
  */
4
4
 
5
+ import { Composite, mergeDefaults } from "@intentius/chant";
6
+ import {
7
+ CloudFunction as CloudFunctionResource,
8
+ StorageBucket,
9
+ IAMPolicyMember,
10
+ } from "../generated";
11
+
5
12
  export interface CloudFunctionWithTriggerProps {
6
13
  /** Function name. */
7
14
  name: string;
@@ -27,15 +34,15 @@ export interface CloudFunctionWithTriggerProps {
27
34
  labels?: Record<string, string>;
28
35
  /** Namespace for all resources. */
29
36
  namespace?: string;
37
+ /** Per-member defaults for customizing individual resources. */
38
+ defaults?: {
39
+ function?: Partial<ConstructorParameters<typeof CloudFunctionResource>[0]>;
40
+ sourceBucket?: Partial<ConstructorParameters<typeof StorageBucket>[0]>;
41
+ invokerIam?: Partial<ConstructorParameters<typeof IAMPolicyMember>[0]>;
42
+ };
30
43
  }
31
44
 
32
- export interface CloudFunctionWithTriggerResult {
33
- function: Record<string, unknown>;
34
- sourceBucket: Record<string, unknown>;
35
- invokerIam?: Record<string, unknown>;
36
- }
37
-
38
- export function CloudFunctionWithTrigger(props: CloudFunctionWithTriggerProps): CloudFunctionWithTriggerResult {
45
+ export const CloudFunctionWithTrigger = Composite<CloudFunctionWithTriggerProps>((props) => {
39
46
  const {
40
47
  name,
41
48
  runtime,
@@ -49,6 +56,7 @@ export function CloudFunctionWithTrigger(props: CloudFunctionWithTriggerProps):
49
56
  environmentVariables,
50
57
  labels: extraLabels = {},
51
58
  namespace,
59
+ defaults: defs,
52
60
  } = props;
53
61
 
54
62
  const commonLabels: Record<string, string> = {
@@ -57,7 +65,7 @@ export function CloudFunctionWithTrigger(props: CloudFunctionWithTriggerProps):
57
65
  ...extraLabels,
58
66
  };
59
67
 
60
- const sourceBucket: Record<string, unknown> = {
68
+ const sourceBucket = new StorageBucket(mergeDefaults({
61
69
  metadata: {
62
70
  name: `${name}-source`,
63
71
  ...(namespace && { namespace }),
@@ -65,13 +73,13 @@ export function CloudFunctionWithTrigger(props: CloudFunctionWithTriggerProps):
65
73
  },
66
74
  location: region ?? "US",
67
75
  uniformBucketLevelAccess: true,
68
- };
76
+ } as Record<string, unknown>, defs?.sourceBucket));
69
77
 
70
78
  const eventTrigger = triggerType === "pubsub" && triggerTopic
71
79
  ? { eventType: "google.cloud.pubsub.topic.v1.messagePublished", pubsubTopic: triggerTopic }
72
80
  : undefined;
73
81
 
74
- const fn: Record<string, unknown> = {
82
+ const fn = new CloudFunctionResource(mergeDefaults({
75
83
  metadata: {
76
84
  name,
77
85
  ...(namespace && { namespace }),
@@ -89,15 +97,15 @@ export function CloudFunctionWithTrigger(props: CloudFunctionWithTriggerProps):
89
97
  },
90
98
  ...(eventTrigger && { eventTrigger }),
91
99
  ...(environmentVariables && { environmentVariables }),
92
- };
100
+ } as Record<string, unknown>, defs?.function));
93
101
 
94
- const result: CloudFunctionWithTriggerResult = {
102
+ const result: Record<string, any> = {
95
103
  function: fn,
96
104
  sourceBucket,
97
105
  };
98
106
 
99
107
  if (publicAccess && triggerType === "http") {
100
- result.invokerIam = {
108
+ result.invokerIam = new IAMPolicyMember(mergeDefaults({
101
109
  metadata: {
102
110
  name: `${name}-invoker`,
103
111
  ...(namespace && { namespace }),
@@ -110,8 +118,8 @@ export function CloudFunctionWithTrigger(props: CloudFunctionWithTriggerProps):
110
118
  kind: "CloudFunctionsFunction",
111
119
  name,
112
120
  },
113
- };
121
+ } as Record<string, unknown>, defs?.invokerIam));
114
122
  }
115
123
 
116
124
  return result;
117
- }
125
+ }, "CloudFunctionWithTrigger");