@intentius/chant-lexicon-gcp 0.14.0 → 0.15.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.
- package/dist/integrity.json +2 -2
- package/dist/lint/audit-catalog.d.ts +7 -0
- package/dist/lint/audit-catalog.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/op/activities/gcp-apply.d.ts +278 -0
- package/dist/op/activities/gcp-apply.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +9 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/plugin.d.ts.map +1 -1
- package/package.json +7 -2
- package/src/describe-resources.ts +3 -3
- package/src/import/live-export.ts +2 -2
- package/src/lint/audit-catalog.ts +33 -0
- package/src/op/activities/gcp-apply.test.ts +475 -0
- package/src/op/activities/gcp-apply.ts +701 -0
- package/src/op/activities/index.ts +46 -0
- package/src/plugin.ts +9 -0
- package/src/serializer.ts +2 -2
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "83a16761b52ff3f8ad88e37eccca05dfc72a28e6e290d7aa0bba0e2d53911ca9",
|
|
5
5
|
"meta.json": "475f10909854d059a2e2d4fb541c058803422e38b9c64bd4d6858c0623eedb8c",
|
|
6
6
|
"types/index.d.ts": "5433b54c04b57d6fda8f3d81ef92ed0aca87f7e8e5dd3ea92d8de6e62a3ccfab",
|
|
7
7
|
"rules/hardcoded-project.ts": "228631d3159e1ffcce2359c66073d4ae59bb0285f378e46e446f416aec50481c",
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"skills/chant-gcp-patterns.md": "a7ef31c1eb2f7244d3f73952c300472ef94c1eb09bd7a1003281b89299b6b704",
|
|
38
38
|
"skills/chant-gcp-gke.md": "be277019da9a722c851e47cd2dfb9c9536668948c3535fb20db7697e934c4e2b"
|
|
39
39
|
},
|
|
40
|
-
"composite": "
|
|
40
|
+
"composite": "1debcc99b19499e7ec9f42ec39eeafe752a818018119869dc2cef6e31419bd34"
|
|
41
41
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gcp lexicon's `chant audit` catalog — metadata for the WGC* Config
|
|
3
|
+
* Connector post-synth rules. Contributed via `gcpPlugin.auditCatalog()` (#687).
|
|
4
|
+
*/
|
|
5
|
+
import { type RuleMeta } from "@intentius/chant/audit/catalog";
|
|
6
|
+
export declare const gcpAuditCatalog: Record<string, RuleMeta>;
|
|
7
|
+
//# sourceMappingURL=audit-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAa,KAAK,QAAQ,EAAkB,MAAM,gCAAgC,CAAC;AAI1F,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAwBpD,CAAC"}
|
package/dist/manifest.json
CHANGED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/** The GCP labels chant stamps on resources it creates. */
|
|
2
|
+
export declare function chantOwnershipLabels(): Record<string, string>;
|
|
3
|
+
/** True when a live resource's labels carry chant's ownership marker. */
|
|
4
|
+
export declare function isChantOwned(labels: Record<string, string> | null | undefined): boolean;
|
|
5
|
+
/** Minimal shape of a serialized CNRM resource (`*.cnrm.cloud.google.com`). */
|
|
6
|
+
export interface GcpResource {
|
|
7
|
+
apiVersion?: string;
|
|
8
|
+
kind?: string;
|
|
9
|
+
metadata?: {
|
|
10
|
+
name?: string;
|
|
11
|
+
labels?: Record<string, string>;
|
|
12
|
+
annotations?: Record<string, string>;
|
|
13
|
+
};
|
|
14
|
+
spec?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
/** A CNRM StorageBucket (`storage.cnrm.cloud.google.com`). */
|
|
17
|
+
export interface CnrmStorageBucket extends GcpResource {
|
|
18
|
+
spec?: {
|
|
19
|
+
location?: string;
|
|
20
|
+
storageClass?: string;
|
|
21
|
+
uniformBucketLevelAccess?: boolean;
|
|
22
|
+
versioning?: {
|
|
23
|
+
enabled?: boolean;
|
|
24
|
+
};
|
|
25
|
+
lifecycleRule?: Array<{
|
|
26
|
+
action?: Record<string, unknown>;
|
|
27
|
+
condition?: Record<string, unknown>;
|
|
28
|
+
}>;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/** GCS Buckets:insert request body (the subset chant emits). */
|
|
32
|
+
export interface BucketInsertBody {
|
|
33
|
+
name: string;
|
|
34
|
+
location?: string;
|
|
35
|
+
storageClass?: string;
|
|
36
|
+
iamConfiguration?: {
|
|
37
|
+
uniformBucketLevelAccess: {
|
|
38
|
+
enabled: boolean;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
versioning?: {
|
|
42
|
+
enabled: boolean;
|
|
43
|
+
};
|
|
44
|
+
lifecycle?: {
|
|
45
|
+
rule: Array<{
|
|
46
|
+
action: Record<string, unknown>;
|
|
47
|
+
condition?: Record<string, unknown>;
|
|
48
|
+
}>;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Pub/Sub Topic resource body (subset). */
|
|
52
|
+
export interface PubSubTopicBody {
|
|
53
|
+
labels?: Record<string, string>;
|
|
54
|
+
messageRetentionDuration?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* One HTTP plan: an idempotency `GET` (200 = exists), the create request, and —
|
|
58
|
+
* when the resource supports in-place reconcile — the update request. A mapper
|
|
59
|
+
* that omits `update` leaves an existing resource untouched (skip-if-exists).
|
|
60
|
+
*/
|
|
61
|
+
export interface ApplyPlan {
|
|
62
|
+
getUrl: string;
|
|
63
|
+
create: {
|
|
64
|
+
method: "POST" | "PUT";
|
|
65
|
+
url: string;
|
|
66
|
+
body: unknown;
|
|
67
|
+
};
|
|
68
|
+
update?: {
|
|
69
|
+
method: "PATCH" | "PUT";
|
|
70
|
+
url: string;
|
|
71
|
+
body: unknown;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Handles an async create whose response is a long-running operation. Present
|
|
76
|
+
* only on mappers for resources that provision asynchronously (Cloud Run, GKE,
|
|
77
|
+
* Cloud SQL, …); synchronous resources (buckets, topics) omit it.
|
|
78
|
+
*/
|
|
79
|
+
export interface OperationSpec {
|
|
80
|
+
/**
|
|
81
|
+
* URL to poll from the create response, or `undefined` when the create
|
|
82
|
+
* completed synchronously (returned the resource, not an operation).
|
|
83
|
+
*/
|
|
84
|
+
pollUrl(createResponseBody: unknown, ctx: {
|
|
85
|
+
base: string;
|
|
86
|
+
project: string;
|
|
87
|
+
}): string | undefined;
|
|
88
|
+
/** True once the polled operation body reports completion. */
|
|
89
|
+
isDone(operationBody: unknown): boolean;
|
|
90
|
+
/** An error message from a completed-with-error operation, else undefined. */
|
|
91
|
+
error(operationBody: unknown): string | undefined;
|
|
92
|
+
}
|
|
93
|
+
/** How to list live resources of a kind, so `prune` can find chant-owned orphans. */
|
|
94
|
+
export interface ListSpec {
|
|
95
|
+
/** LIST endpoint for the kind. */
|
|
96
|
+
url(ctx: {
|
|
97
|
+
base: string;
|
|
98
|
+
project: string;
|
|
99
|
+
}): string;
|
|
100
|
+
/** Extract `{ name, labels }` for each item from the LIST response body. */
|
|
101
|
+
items(responseBody: unknown): Array<{
|
|
102
|
+
name: string;
|
|
103
|
+
labels?: Record<string, string> | null;
|
|
104
|
+
}>;
|
|
105
|
+
}
|
|
106
|
+
/** Maps one CNRM kind to its GCP REST operations. The unit of the dispatch table. */
|
|
107
|
+
export interface ResourceMapper {
|
|
108
|
+
/** CNRM kind this handles. */
|
|
109
|
+
kind: string;
|
|
110
|
+
/** Real-GCP API host, used when no `endpoint` override is given. */
|
|
111
|
+
defaultHost: string;
|
|
112
|
+
/** Build the GET + create plan for one resource against `base`. */
|
|
113
|
+
plan(resource: GcpResource, ctx: {
|
|
114
|
+
base: string;
|
|
115
|
+
project: string;
|
|
116
|
+
}): ApplyPlan;
|
|
117
|
+
/** Present for async resources whose create returns a long-running operation. */
|
|
118
|
+
operation?: OperationSpec;
|
|
119
|
+
/** Present when the kind can be pruned (list live → delete chant-owned orphans). */
|
|
120
|
+
list?: ListSpec;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A standard `google.longrunning` operation spec: the create response carries an
|
|
124
|
+
* operation `name` (`projects/…/operations/…`), polled at `{base}/{version}/{name}`
|
|
125
|
+
* until `done: true`. Reused by every service that follows the pattern.
|
|
126
|
+
*/
|
|
127
|
+
export declare function longRunningOperation(version: string): OperationSpec;
|
|
128
|
+
/**
|
|
129
|
+
* Map a CNRM StorageBucket to a GCS Buckets:insert body. Pure. CNRM mirrors the
|
|
130
|
+
* Terraform google provider which mirrors the GCS API, so most fields are
|
|
131
|
+
* field-for-field; the renames are `uniformBucketLevelAccess` →
|
|
132
|
+
* `iamConfiguration.uniformBucketLevelAccess.enabled` and `lifecycleRule` →
|
|
133
|
+
* `lifecycle.rule`.
|
|
134
|
+
*/
|
|
135
|
+
export declare function bucketInsertBody(cnrm: CnrmStorageBucket): BucketInsertBody;
|
|
136
|
+
/** Map a CNRM PubSubTopic to a Pub/Sub Topic body. Pure. Name travels in the URL. */
|
|
137
|
+
export declare function pubSubTopicBody(resource: GcpResource): PubSubTopicBody;
|
|
138
|
+
export declare const storageBucketMapper: ResourceMapper;
|
|
139
|
+
export declare const pubSubTopicMapper: ResourceMapper;
|
|
140
|
+
/** Map a CNRM RunService to a Cloud Run v2 Service body. Pure. */
|
|
141
|
+
export declare function cloudRunServiceBody(resource: GcpResource): {
|
|
142
|
+
template?: unknown;
|
|
143
|
+
};
|
|
144
|
+
export declare const cloudRunServiceMapper: ResourceMapper;
|
|
145
|
+
/**
|
|
146
|
+
* Map a CNRM PubSubSubscription to a Pub/Sub Subscription body, resolving the
|
|
147
|
+
* `topicRef` to a full topic path. Pure.
|
|
148
|
+
*/
|
|
149
|
+
export declare function pubSubSubscriptionBody(resource: GcpResource, project: string): Record<string, unknown>;
|
|
150
|
+
export declare const pubSubSubscriptionMapper: ResourceMapper;
|
|
151
|
+
/** Map a CNRM SecretManagerSecret to a Secret Manager create body. Pure. */
|
|
152
|
+
export declare function secretBody(resource: GcpResource): Record<string, unknown>;
|
|
153
|
+
export declare const secretManagerSecretMapper: ResourceMapper;
|
|
154
|
+
/** Map a CNRM IAMServiceAccount to a service-account create body. Pure. */
|
|
155
|
+
export declare function serviceAccountBody(resource: GcpResource, accountId: string): Record<string, unknown>;
|
|
156
|
+
export declare const gcpServiceAccountMapper: ResourceMapper;
|
|
157
|
+
/** The kind → mapper dispatch table. Adding a resource type is a new entry here. */
|
|
158
|
+
export declare const MAPPERS: Record<string, ResourceMapper>;
|
|
159
|
+
/**
|
|
160
|
+
* Collect the local resource names this resource references via CNRM `*Ref`
|
|
161
|
+
* fields (`topicRef: { name }`, `subnetworkRefs: [{ name }]`, …). `external`
|
|
162
|
+
* refs point outside the manifest and are ignored for ordering. Pure.
|
|
163
|
+
*/
|
|
164
|
+
export declare function referencedNames(resource: GcpResource): string[];
|
|
165
|
+
/**
|
|
166
|
+
* Topologically order resources so a referenced resource comes before the
|
|
167
|
+
* resource that references it (apply order; reverse for delete). Refs to names
|
|
168
|
+
* not in the manifest are left to exist already. Throws on a reference cycle.
|
|
169
|
+
* Pure — a DFS post-order.
|
|
170
|
+
*/
|
|
171
|
+
export declare function orderByReferences(resources: GcpResource[]): GcpResource[];
|
|
172
|
+
/** Resolve the project: `GOOGLE_CLOUD_PROJECT` env, else the CNRM project-id annotation. Pure. */
|
|
173
|
+
export declare function resolveGcpProject(resource: GcpResource, env?: NodeJS.ProcessEnv): string;
|
|
174
|
+
/** Injectable HTTP client — mirrors argo's injectable fetcher so tests avoid the network. */
|
|
175
|
+
export type GcpHttp = (method: string, url: string, body?: unknown, signal?: AbortSignal) => Promise<{
|
|
176
|
+
status: number;
|
|
177
|
+
text: string;
|
|
178
|
+
}>;
|
|
179
|
+
/**
|
|
180
|
+
* Reconcile one resource via its mapper: `GET` it; if present, PATCH it to the
|
|
181
|
+
* desired state (when the mapper supports `update`, else leave it); if absent,
|
|
182
|
+
* create it. Async create/update return an operation that is polled to
|
|
183
|
+
* completion. `http` is injectable for tests.
|
|
184
|
+
*/
|
|
185
|
+
export declare function applyResource(mapper: ResourceMapper, resource: GcpResource, ctx: {
|
|
186
|
+
base: string;
|
|
187
|
+
project: string;
|
|
188
|
+
}, http?: GcpHttp, signal?: AbortSignal): Promise<{
|
|
189
|
+
kind: string;
|
|
190
|
+
name: string;
|
|
191
|
+
created: boolean;
|
|
192
|
+
updated: boolean;
|
|
193
|
+
}>;
|
|
194
|
+
/**
|
|
195
|
+
* Poll a long-running operation until it reports done (or errors / times out).
|
|
196
|
+
* `http` and the interval are injectable so tests drive it without real waits.
|
|
197
|
+
*/
|
|
198
|
+
export declare function waitForOperation(op: OperationSpec, pollUrl: string, http?: GcpHttp, signal?: AbortSignal, opts?: {
|
|
199
|
+
intervalMs?: number;
|
|
200
|
+
timeoutMs?: number;
|
|
201
|
+
}): Promise<void>;
|
|
202
|
+
/** Parse a built manifest into CNRM resource objects — YAML (multi-doc) or JSON. Pure. */
|
|
203
|
+
export declare function parseManifest(content: string, path: string): GcpResource[];
|
|
204
|
+
export interface GcpApplyArgs {
|
|
205
|
+
/** Path to a built CNRM manifest (YAML multi-doc or JSON). */
|
|
206
|
+
manifestPath: string;
|
|
207
|
+
/** GCS/GCP endpoint override for all kinds (e.g. floci-gcp `:4588`). Default: each kind's real-GCP host. */
|
|
208
|
+
endpoint?: string;
|
|
209
|
+
/** Project override. Default: `GOOGLE_CLOUD_PROJECT` env / CNRM annotation. */
|
|
210
|
+
project?: string;
|
|
211
|
+
/**
|
|
212
|
+
* Delete chant-owned resources of a manifested kind that are no longer in the
|
|
213
|
+
* manifest (owned-only prune). Destructive — off by default. Only kinds with a
|
|
214
|
+
* `list` mapper are pruned; foreign (non-chant) resources are never touched.
|
|
215
|
+
*/
|
|
216
|
+
prune?: boolean;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Owned-only prune: for each manifested kind with `list` support, delete the
|
|
220
|
+
* chant-owned live resources whose name is not in `desiredByKind`. Foreign
|
|
221
|
+
* resources (no ownership label) are left alone.
|
|
222
|
+
*/
|
|
223
|
+
export declare function pruneOrphans(desired: GcpResource[], resolve: (mapper: ResourceMapper, resource?: GcpResource) => {
|
|
224
|
+
base: string;
|
|
225
|
+
project: string;
|
|
226
|
+
}, http?: GcpHttp, signal?: AbortSignal): Promise<Array<{
|
|
227
|
+
kind: string;
|
|
228
|
+
name: string;
|
|
229
|
+
deleted: boolean;
|
|
230
|
+
}>>;
|
|
231
|
+
/**
|
|
232
|
+
* The native GCP applier (#706) — read a built CNRM manifest and apply each
|
|
233
|
+
* resource directly to its GCP REST API, targeting a local floci-gcp emulator or
|
|
234
|
+
* real GCP by endpoint override. Unlike AWS/Azure/k8s, GCP has no native deploy
|
|
235
|
+
* service to shell out to, so chant maps each `kind` to a REST call itself (the
|
|
236
|
+
* `MAPPERS` dispatch table). Unknown kinds are skipped. Uses longInfra profile.
|
|
237
|
+
* `http` is injectable for tests.
|
|
238
|
+
*/
|
|
239
|
+
export declare function gcpApply(args: GcpApplyArgs, signal?: AbortSignal, http?: GcpHttp): Promise<{
|
|
240
|
+
applied: Array<{
|
|
241
|
+
kind: string;
|
|
242
|
+
name: string;
|
|
243
|
+
created: boolean;
|
|
244
|
+
updated: boolean;
|
|
245
|
+
}>;
|
|
246
|
+
pruned: Array<{
|
|
247
|
+
kind: string;
|
|
248
|
+
name: string;
|
|
249
|
+
deleted: boolean;
|
|
250
|
+
}>;
|
|
251
|
+
}>;
|
|
252
|
+
/**
|
|
253
|
+
* Idempotently delete one resource: `DELETE` its resource URL. A 404 means it is
|
|
254
|
+
* already gone. For async resources, `DELETE` returns a long-running operation
|
|
255
|
+
* that is polled to completion. `http` is injectable for tests.
|
|
256
|
+
*/
|
|
257
|
+
export declare function deleteResource(mapper: ResourceMapper, resource: GcpResource, ctx: {
|
|
258
|
+
base: string;
|
|
259
|
+
project: string;
|
|
260
|
+
}, http?: GcpHttp, signal?: AbortSignal): Promise<{
|
|
261
|
+
kind: string;
|
|
262
|
+
name: string;
|
|
263
|
+
deleted: boolean;
|
|
264
|
+
}>;
|
|
265
|
+
/**
|
|
266
|
+
* The inverse of {@link gcpApply} — read a built CNRM manifest and delete the
|
|
267
|
+
* resources it declares (in reverse order, so dependents go before their
|
|
268
|
+
* dependencies). Idempotent: already-absent resources are a no-op. Uses
|
|
269
|
+
* longInfra profile. `http` is injectable for tests.
|
|
270
|
+
*/
|
|
271
|
+
export declare function gcpDelete(args: GcpApplyArgs, signal?: AbortSignal, http?: GcpHttp): Promise<{
|
|
272
|
+
deleted: Array<{
|
|
273
|
+
kind: string;
|
|
274
|
+
name: string;
|
|
275
|
+
deleted: boolean;
|
|
276
|
+
}>;
|
|
277
|
+
}>;
|
|
278
|
+
//# sourceMappingURL=gcp-apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gcp-apply.d.ts","sourceRoot":"","sources":["../../../src/op/activities/gcp-apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GCP Op activities — resolved by the core activity registry when a project's
|
|
3
|
+
* `chant.config.ts` lists the `gcp` lexicon. Contributes the native GCP applier
|
|
4
|
+
* (`gcpApply`), which maps CNRM resources to GCP REST calls since GCP has no
|
|
5
|
+
* native deployment service to shell out to.
|
|
6
|
+
*/
|
|
7
|
+
export { gcpApply, gcpDelete, applyResource, deleteResource, waitForOperation, longRunningOperation, bucketInsertBody, pubSubTopicBody, pubSubSubscriptionBody, cloudRunServiceBody, secretBody, serviceAccountBody, storageBucketMapper, pubSubTopicMapper, pubSubSubscriptionMapper, cloudRunServiceMapper, secretManagerSecretMapper, gcpServiceAccountMapper, MAPPERS, referencedNames, orderByReferences, pruneOrphans, chantOwnershipLabels, isChantOwned, resolveGcpProject, parseManifest, } from "./gcp-apply.js";
|
|
8
|
+
export type { GcpApplyArgs, GcpResource, CnrmStorageBucket, BucketInsertBody, PubSubTopicBody, ResourceMapper, OperationSpec, ListSpec, ApplyPlan, GcpHttp, } from "./gcp-apply.js";
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,QAAQ,EACR,SAAS,EACT,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,UAAU,EACV,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,uBAAuB,EACvB,OAAO,EACP,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,aAAa,EACb,QAAQ,EACR,SAAS,EACT,OAAO,GACR,MAAM,aAAa,CAAC"}
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAqC,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAqC,MAAM,0BAA0B,CAAC;AAoBjG,eAAO,MAAM,SAAS,EAAE,aAuWvB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-gcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.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",
|
|
@@ -44,6 +44,11 @@
|
|
|
44
44
|
"types": "./dist/detect.d.ts",
|
|
45
45
|
"default": "./src/detect.ts"
|
|
46
46
|
},
|
|
47
|
+
"./op/activities": {
|
|
48
|
+
"development": "./src/op/activities/index.ts",
|
|
49
|
+
"types": "./dist/op/activities/index.d.ts",
|
|
50
|
+
"default": "./src/op/activities/index.ts"
|
|
51
|
+
},
|
|
47
52
|
"./lint/post-synth": {
|
|
48
53
|
"development": "./src/lint/post-synth/index.ts",
|
|
49
54
|
"types": "./dist/lint/post-synth/index.d.ts",
|
|
@@ -71,6 +76,6 @@
|
|
|
71
76
|
"typescript": "^5.9.3"
|
|
72
77
|
},
|
|
73
78
|
"peerDependencies": {
|
|
74
|
-
"@intentius/chant": "^0.
|
|
79
|
+
"@intentius/chant": "^0.15.0"
|
|
75
80
|
}
|
|
76
81
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { exec } from "node:child_process";
|
|
16
16
|
import { promisify } from "node:util";
|
|
17
17
|
import type { ResourceMetadata } from "@intentius/chant/lexicon";
|
|
18
|
-
import { hasOwnershipMarker, classifyOwnership } from "@intentius/chant/ownership";
|
|
18
|
+
import { hasOwnershipMarker, classifyOwnership, LABEL_OWNERSHIP_KEYS } from "@intentius/chant/ownership";
|
|
19
19
|
|
|
20
20
|
const execAsync = promisify(exec);
|
|
21
21
|
|
|
@@ -101,7 +101,7 @@ export async function describeResources(options: {
|
|
|
101
101
|
const { stdout } = await execAsync(cmd);
|
|
102
102
|
const obj: KubectlResponse = JSON.parse(stdout);
|
|
103
103
|
// owned filter: skip resources not carrying chant's marker label.
|
|
104
|
-
if (options.owned && !hasOwnershipMarker(obj.metadata?.labels,
|
|
104
|
+
if (options.owned && !hasOwnershipMarker(obj.metadata?.labels, LABEL_OWNERSHIP_KEYS)) {
|
|
105
105
|
continue;
|
|
106
106
|
}
|
|
107
107
|
result[entityName] = {
|
|
@@ -109,7 +109,7 @@ export async function describeResources(options: {
|
|
|
109
109
|
physicalId: obj.metadata?.uid,
|
|
110
110
|
status: statusFromCC(obj),
|
|
111
111
|
lastUpdated: obj.metadata?.creationTimestamp,
|
|
112
|
-
ownership: classifyOwnership(obj.metadata?.labels,
|
|
112
|
+
ownership: classifyOwnership(obj.metadata?.labels, LABEL_OWNERSHIP_KEYS),
|
|
113
113
|
attributes: pruneUndefined({
|
|
114
114
|
namespace: obj.metadata?.namespace,
|
|
115
115
|
labels: obj.metadata?.labels,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExportedTemplate, ResourceSelector } from "@intentius/chant/lexicon";
|
|
2
|
-
import { hasOwnershipMarker } from "@intentius/chant/ownership";
|
|
2
|
+
import { hasOwnershipMarker, LABEL_OWNERSHIP_KEYS } from "@intentius/chant/ownership";
|
|
3
3
|
import { GcpParser } from "./parser";
|
|
4
4
|
|
|
5
5
|
/** Server-written metadata fields, stripped unless `verbatim`. */
|
|
@@ -61,7 +61,7 @@ export function buildExportFromObjects(
|
|
|
61
61
|
? objects.filter((o) => {
|
|
62
62
|
if (!isRecord(o)) return false;
|
|
63
63
|
const labels = isRecord(o.metadata) ? (o.metadata.labels as Record<string, unknown>) : undefined;
|
|
64
|
-
return hasOwnershipMarker(labels,
|
|
64
|
+
return hasOwnershipMarker(labels, LABEL_OWNERSHIP_KEYS);
|
|
65
65
|
})
|
|
66
66
|
: objects;
|
|
67
67
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gcp lexicon's `chant audit` catalog — metadata for the WGC* Config
|
|
3
|
+
* Connector post-synth rules. Contributed via `gcpPlugin.auditCatalog()` (#687).
|
|
4
|
+
*/
|
|
5
|
+
import { auditRule, type RuleMeta, type Authority } from "@intentius/chant/audit/catalog";
|
|
6
|
+
|
|
7
|
+
const GCP_SEC: Authority = { name: "Google Cloud — Security best practices", url: "https://cloud.google.com/security/best-practices" };
|
|
8
|
+
|
|
9
|
+
export const gcpAuditCatalog: Record<string, RuleMeta> = {
|
|
10
|
+
WGC101: auditRule("WGC101", "merge-worthy", "guidance", "Storage/SQL without encryption configuration", "Configure encryption (e.g. a CMEK key) for data at rest.", { authority: [GCP_SEC] }),
|
|
11
|
+
WGC102: auditRule("WGC102", "merge-worthy", "guidance", "Public IAM member (allUsers/allAuthenticatedUsers)", "Remove allUsers/allAuthenticatedUsers bindings.", { authority: [GCP_SEC] }),
|
|
12
|
+
WGC103: auditRule("WGC103", "report-only", "guidance", "Missing project-id annotation", "Add the cnrm.cloud.google.com/project-id annotation.", { category: "best-practice" }),
|
|
13
|
+
WGC104: auditRule("WGC104", "merge-worthy", "guidance", "Bucket without uniform bucket-level access", "Enable uniformBucketLevelAccess.", { authority: [GCP_SEC] }),
|
|
14
|
+
WGC105: auditRule("WGC105", "merge-worthy", "guidance", "Cloud SQL open to 0.0.0.0/0", "Restrict authorizedNetworks to known sources.", { authority: [GCP_SEC] }),
|
|
15
|
+
WGC106: auditRule("WGC106", "report-only", "guidance", "Missing deletion-policy annotation", "Add the cnrm.cloud.google.com/deletion-policy annotation.", { category: "best-practice" }),
|
|
16
|
+
WGC107: auditRule("WGC107", "report-only", "guidance", "Bucket versioning disabled", "Enable object versioning.", { category: "best-practice" }),
|
|
17
|
+
WGC108: auditRule("WGC108", "report-only", "guidance", "Cloud SQL backups disabled", "Enable backup configuration.", { category: "best-practice" }),
|
|
18
|
+
WGC109: auditRule("WGC109", "merge-worthy", "guidance", "Firewall open to 0.0.0.0/0", "Restrict sourceRanges to known sources.", { authority: [GCP_SEC] }),
|
|
19
|
+
WGC110: auditRule("WGC110", "merge-worthy", "guidance", "KMS key without rotation", "Set a rotationPeriod on the CryptoKey.", { authority: [GCP_SEC] }),
|
|
20
|
+
WGC111: auditRule("WGC111", "merge-worthy", "guidance", "Reference to an undefined resource", "Point the reference at a resource in the output.", { category: "correctness" }),
|
|
21
|
+
WGC112: auditRule("WGC112", "merge-worthy", "guidance", "Missing or invalid apiVersion", "Set a valid cnrm.cloud.google.com apiVersion.", { category: "correctness" }),
|
|
22
|
+
WGC113: auditRule("WGC113", "report-only", "guidance", "Alpha API version", "Move to a beta/GA API version.", { category: "best-practice" }),
|
|
23
|
+
WGC201: auditRule("WGC201", "report-only", "guidance", "Missing managed-by label", "Add the app.kubernetes.io/managed-by label.", { category: "best-practice" }),
|
|
24
|
+
WGC202: auditRule("WGC202", "merge-worthy", "guidance", "Cluster without Workload Identity", "Enable Workload Identity on the ContainerCluster.", { authority: [GCP_SEC] }),
|
|
25
|
+
WGC203: auditRule("WGC203", "merge-worthy", "guidance", "Node pool uses broad cloud-platform scope", "Use narrowly-scoped OAuth scopes instead of cloud-platform.", { authority: [GCP_SEC] }),
|
|
26
|
+
WGC204: auditRule("WGC204", "report-only", "guidance", "Compute instance without Shielded VM", "Enable Shielded VM configuration.", { category: "best-practice" }),
|
|
27
|
+
WGC301: auditRule("WGC301", "report-only", "guidance", "No IAMAuditConfig found", "Configure audit logging via IAMAuditConfig.", { category: "best-practice" }),
|
|
28
|
+
WGC302: auditRule("WGC302", "report-only", "guidance", "No Service (enabled APIs) found", "Declare the GCP APIs you depend on.", { category: "best-practice" }),
|
|
29
|
+
WGC303: auditRule("WGC303", "report-only", "guidance", "No VPC Service Controls perimeter", "Consider an AccessContextManager ServicePerimeter.", { category: "best-practice" }),
|
|
30
|
+
WGC401: auditRule("WGC401", "merge-worthy", "guidance", "Unknown field in resource spec", "Remove the unknown spec field.", { category: "correctness" }),
|
|
31
|
+
WGC402: auditRule("WGC402", "merge-worthy", "guidance", "Missing required spec field", "Add the required spec field.", { category: "correctness" }),
|
|
32
|
+
WGC403: auditRule("WGC403", "merge-worthy", "guidance", "Spec field has wrong type/structure", "Fix the field's type/structure.", { category: "correctness" }),
|
|
33
|
+
};
|