@intentius/chant-lexicon-gcp 0.0.15
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 +36 -0
- package/dist/manifest.json +12 -0
- package/dist/meta.json +10919 -0
- package/dist/rules/gcp-helpers.ts +117 -0
- package/dist/rules/hardcoded-project.ts +58 -0
- package/dist/rules/hardcoded-region.ts +56 -0
- package/dist/rules/public-iam.ts +43 -0
- package/dist/rules/wgc101.ts +56 -0
- package/dist/rules/wgc102.ts +35 -0
- package/dist/rules/wgc103.ts +45 -0
- package/dist/rules/wgc104.ts +42 -0
- package/dist/rules/wgc105.ts +46 -0
- package/dist/rules/wgc106.ts +36 -0
- package/dist/rules/wgc107.ts +39 -0
- package/dist/rules/wgc108.ts +41 -0
- package/dist/rules/wgc109.ts +39 -0
- package/dist/rules/wgc110.ts +38 -0
- package/dist/rules/wgc111.ts +54 -0
- package/dist/rules/wgc112.ts +56 -0
- package/dist/rules/wgc113.ts +42 -0
- package/dist/rules/wgc201.ts +36 -0
- package/dist/rules/wgc202.ts +39 -0
- package/dist/rules/wgc203.ts +44 -0
- package/dist/rules/wgc204.ts +39 -0
- package/dist/rules/wgc301.ts +34 -0
- package/dist/rules/wgc302.ts +34 -0
- package/dist/rules/wgc303.ts +37 -0
- package/dist/skills/chant-gcp-patterns.md +367 -0
- package/dist/skills/chant-gcp-security.md +276 -0
- package/dist/skills/chant-gcp.md +108 -0
- package/dist/types/index.d.ts +26529 -0
- package/package.json +35 -0
- package/src/actions/index.ts +52 -0
- package/src/codegen/docs-cli.ts +7 -0
- package/src/codegen/docs.ts +820 -0
- package/src/codegen/generate-cli.ts +24 -0
- package/src/codegen/generate.ts +252 -0
- package/src/codegen/naming.test.ts +49 -0
- package/src/codegen/naming.ts +132 -0
- package/src/codegen/package.ts +66 -0
- package/src/composites/cloud-function.ts +117 -0
- package/src/composites/cloud-run-service.ts +124 -0
- package/src/composites/cloud-sql-instance.ts +126 -0
- package/src/composites/composites.test.ts +432 -0
- package/src/composites/gcs-bucket.ts +111 -0
- package/src/composites/gke-cluster.ts +125 -0
- package/src/composites/index.ts +20 -0
- package/src/composites/managed-certificate.ts +79 -0
- package/src/composites/private-service.ts +95 -0
- package/src/composites/pubsub-pipeline.ts +102 -0
- package/src/composites/secure-project.ts +128 -0
- package/src/composites/vpc-network.ts +165 -0
- package/src/coverage.test.ts +27 -0
- package/src/coverage.ts +51 -0
- package/src/default-labels.test.ts +111 -0
- package/src/default-labels.ts +93 -0
- package/src/generated/index.d.ts +26529 -0
- package/src/generated/index.ts +1723 -0
- package/src/generated/lexicon-gcp.json +10919 -0
- package/src/generated/runtime.ts +4 -0
- package/src/import/generator.test.ts +125 -0
- package/src/import/generator.ts +82 -0
- package/src/import/parser.test.ts +167 -0
- package/src/import/parser.ts +80 -0
- package/src/import/roundtrip.test.ts +66 -0
- package/src/index.ts +54 -0
- package/src/lint/post-synth/gcp-helpers.ts +117 -0
- package/src/lint/post-synth/index.ts +20 -0
- package/src/lint/post-synth/post-synth.test.ts +693 -0
- package/src/lint/post-synth/wgc101.ts +56 -0
- package/src/lint/post-synth/wgc102.ts +35 -0
- package/src/lint/post-synth/wgc103.ts +45 -0
- package/src/lint/post-synth/wgc104.ts +42 -0
- package/src/lint/post-synth/wgc105.ts +46 -0
- package/src/lint/post-synth/wgc106.ts +36 -0
- package/src/lint/post-synth/wgc107.ts +39 -0
- package/src/lint/post-synth/wgc108.ts +41 -0
- package/src/lint/post-synth/wgc109.ts +39 -0
- package/src/lint/post-synth/wgc110.ts +38 -0
- package/src/lint/post-synth/wgc111.ts +54 -0
- package/src/lint/post-synth/wgc112.ts +56 -0
- package/src/lint/post-synth/wgc113.ts +42 -0
- package/src/lint/post-synth/wgc201.ts +36 -0
- package/src/lint/post-synth/wgc202.ts +39 -0
- package/src/lint/post-synth/wgc203.ts +44 -0
- package/src/lint/post-synth/wgc204.ts +39 -0
- package/src/lint/post-synth/wgc301.ts +34 -0
- package/src/lint/post-synth/wgc302.ts +34 -0
- package/src/lint/post-synth/wgc303.ts +37 -0
- package/src/lint/rules/hardcoded-project.ts +58 -0
- package/src/lint/rules/hardcoded-region.ts +56 -0
- package/src/lint/rules/index.ts +3 -0
- package/src/lint/rules/public-iam.ts +43 -0
- package/src/lint/rules/rules.test.ts +63 -0
- package/src/lsp/completions.test.ts +67 -0
- package/src/lsp/completions.ts +17 -0
- package/src/lsp/hover.test.ts +66 -0
- package/src/lsp/hover.ts +54 -0
- package/src/package-cli.ts +24 -0
- package/src/plugin.test.ts +250 -0
- package/src/plugin.ts +405 -0
- package/src/pseudo.test.ts +40 -0
- package/src/pseudo.ts +19 -0
- package/src/serializer.test.ts +250 -0
- package/src/serializer.ts +232 -0
- package/src/skills/chant-gcp-patterns.md +367 -0
- package/src/skills/chant-gcp-security.md +276 -0
- package/src/skills/chant-gcp.md +108 -0
- package/src/spec/fetch.test.ts +16 -0
- package/src/spec/fetch.ts +121 -0
- package/src/spec/parse.test.ts +163 -0
- package/src/spec/parse.ts +432 -0
- package/src/testdata/compute-instance.yaml +93 -0
- package/src/testdata/iam-policy-member.yaml +66 -0
- package/src/testdata/manifests/compute-instance.yaml +18 -0
- package/src/testdata/manifests/full-app.yaml +34 -0
- package/src/testdata/manifests/storage-bucket.yaml +12 -0
- package/src/testdata/storage-bucket.yaml +100 -0
- package/src/validate-cli.ts +13 -0
- package/src/validate.test.ts +38 -0
- package/src/validate.ts +30 -0
- package/src/variables.ts +15 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CRD parser for GCP Config Connector resources.
|
|
3
|
+
*
|
|
4
|
+
* Adapts the K8s CRD parser pattern for GCP-specific concerns:
|
|
5
|
+
* - Type naming: GCP::{Service}::{Kind}
|
|
6
|
+
* - resourceRef detection
|
|
7
|
+
* - Status field skipping
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import yaml from "js-yaml";
|
|
11
|
+
import type { PropertyConstraints } from "@intentius/chant/codegen/json-schema";
|
|
12
|
+
|
|
13
|
+
// ── Public types ────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
export interface ParsedProperty {
|
|
16
|
+
name: string;
|
|
17
|
+
tsType: string;
|
|
18
|
+
required: boolean;
|
|
19
|
+
description?: string;
|
|
20
|
+
enum?: string[];
|
|
21
|
+
constraints: PropertyConstraints;
|
|
22
|
+
isResourceRef?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ParsedAttribute {
|
|
26
|
+
name: string;
|
|
27
|
+
tsType: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ParsedPropertyType {
|
|
31
|
+
name: string;
|
|
32
|
+
/** Original definition name in the spec (e.g., "networkRef"). */
|
|
33
|
+
specType: string;
|
|
34
|
+
properties: ParsedProperty[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ParsedEnum {
|
|
38
|
+
name: string;
|
|
39
|
+
values: string[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ParsedResource {
|
|
43
|
+
typeName: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
properties: ParsedProperty[];
|
|
46
|
+
attributes: ParsedAttribute[];
|
|
47
|
+
deprecatedProperties: string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface GcpParseResult {
|
|
51
|
+
resource: ParsedResource;
|
|
52
|
+
propertyTypes: ParsedPropertyType[];
|
|
53
|
+
enums: ParsedEnum[];
|
|
54
|
+
gvk: GroupVersionKind;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface GroupVersionKind {
|
|
58
|
+
group: string;
|
|
59
|
+
version: string;
|
|
60
|
+
kind: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── CRD spec types ──────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
interface CRDSpec {
|
|
66
|
+
group: string;
|
|
67
|
+
names: { kind: string; plural?: string; singular?: string };
|
|
68
|
+
versions: Array<{
|
|
69
|
+
name: string;
|
|
70
|
+
served: boolean;
|
|
71
|
+
storage: boolean;
|
|
72
|
+
schema?: { openAPIV3Schema?: OpenAPISchema };
|
|
73
|
+
}>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface OpenAPISchema {
|
|
77
|
+
type?: string;
|
|
78
|
+
description?: string;
|
|
79
|
+
properties?: Record<string, OpenAPISchema>;
|
|
80
|
+
required?: string[];
|
|
81
|
+
items?: OpenAPISchema;
|
|
82
|
+
additionalProperties?: boolean | OpenAPISchema;
|
|
83
|
+
enum?: string[];
|
|
84
|
+
format?: string;
|
|
85
|
+
minimum?: number;
|
|
86
|
+
maximum?: number;
|
|
87
|
+
minLength?: number;
|
|
88
|
+
maxLength?: number;
|
|
89
|
+
pattern?: string;
|
|
90
|
+
default?: unknown;
|
|
91
|
+
"x-kubernetes-preserve-unknown-fields"?: boolean;
|
|
92
|
+
"x-kubernetes-int-or-string"?: boolean;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Naming helpers ──────────────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Extract GCP service name from a CNRM group.
|
|
99
|
+
* "compute.cnrm.cloud.google.com" → "Compute"
|
|
100
|
+
*/
|
|
101
|
+
export function gcpServiceName(group: string): string {
|
|
102
|
+
const firstSegment = group.split(".")[0];
|
|
103
|
+
return firstSegment.charAt(0).toUpperCase() + firstSegment.slice(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Strip the service prefix from a CRD kind.
|
|
108
|
+
* "ComputeInstance", "Compute" → "Instance"
|
|
109
|
+
* "IAMPolicyMember", "Iam" → "PolicyMember"
|
|
110
|
+
*/
|
|
111
|
+
export function stripServicePrefix(kind: string, service: string): string {
|
|
112
|
+
// Direct match (e.g., "Compute" prefix in "ComputeInstance")
|
|
113
|
+
if (kind.startsWith(service)) {
|
|
114
|
+
const rest = kind.slice(service.length);
|
|
115
|
+
if (rest.length > 0 && rest[0] === rest[0].toUpperCase()) {
|
|
116
|
+
return rest;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Case-insensitive match (e.g., "IAM" prefix when service is "Iam")
|
|
120
|
+
const kindLower = kind.toLowerCase();
|
|
121
|
+
const serviceLower = service.toLowerCase();
|
|
122
|
+
if (kindLower.startsWith(serviceLower)) {
|
|
123
|
+
const rest = kind.slice(service.length);
|
|
124
|
+
if (rest.length > 0 && rest[0] === rest[0].toUpperCase()) {
|
|
125
|
+
return rest;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return kind;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Build full GCP type name.
|
|
133
|
+
* "compute.cnrm.cloud.google.com", "ComputeInstance" → "GCP::Compute::Instance"
|
|
134
|
+
*/
|
|
135
|
+
export function gcpTypeName(group: string, kind: string): string {
|
|
136
|
+
const service = gcpServiceName(group);
|
|
137
|
+
const shortKind = stripServicePrefix(kind, service);
|
|
138
|
+
return `GCP::${service}::${shortKind}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Extract short name from a GCP type.
|
|
143
|
+
* "GCP::Compute::Instance" → "Instance"
|
|
144
|
+
*/
|
|
145
|
+
export function gcpShortName(typeName: string): string {
|
|
146
|
+
const parts = typeName.split("::");
|
|
147
|
+
return parts[parts.length - 1];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ── CRD Parsing ─────────────────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Parse a CRD YAML document string into GcpParseResult entries.
|
|
154
|
+
* Supports multi-document YAML for CRD bundles.
|
|
155
|
+
*/
|
|
156
|
+
export function parseGcpCRD(content: string | Buffer): GcpParseResult[] {
|
|
157
|
+
const text = typeof content === "string" ? content : content.toString("utf-8");
|
|
158
|
+
const results: GcpParseResult[] = [];
|
|
159
|
+
|
|
160
|
+
const documents = text
|
|
161
|
+
.split(/^---\s*$/m)
|
|
162
|
+
.map((d) => d.trim())
|
|
163
|
+
.filter((d) => d.length > 0);
|
|
164
|
+
|
|
165
|
+
for (const docStr of documents) {
|
|
166
|
+
const doc = yaml.load(docStr) as Record<string, unknown>;
|
|
167
|
+
if (!doc || doc.kind !== "CustomResourceDefinition") continue;
|
|
168
|
+
|
|
169
|
+
const spec = doc.spec as CRDSpec | undefined;
|
|
170
|
+
if (!spec?.group || !spec?.names?.kind || !spec?.versions) continue;
|
|
171
|
+
|
|
172
|
+
// Only process groups that are Config Connector CRDs
|
|
173
|
+
if (!spec.group.includes("cnrm.cloud.google.com")) continue;
|
|
174
|
+
|
|
175
|
+
const crdResults = parseCRDSpec(spec);
|
|
176
|
+
results.push(...crdResults);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return results;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Parse a CRD spec into GcpParseResult entries.
|
|
184
|
+
*/
|
|
185
|
+
function parseCRDSpec(spec: CRDSpec): GcpParseResult[] {
|
|
186
|
+
const results: GcpParseResult[] = [];
|
|
187
|
+
|
|
188
|
+
// Find the storage version (canonical)
|
|
189
|
+
const storageVersion = spec.versions.find((v) => v.storage && v.served);
|
|
190
|
+
const targetVersion = storageVersion ?? spec.versions.find((v) => v.served);
|
|
191
|
+
if (!targetVersion) return results;
|
|
192
|
+
|
|
193
|
+
const typeName = gcpTypeName(spec.group, spec.names.kind);
|
|
194
|
+
const gvk: GroupVersionKind = {
|
|
195
|
+
group: spec.group,
|
|
196
|
+
version: targetVersion.name,
|
|
197
|
+
kind: spec.names.kind,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const schema = targetVersion.schema?.openAPIV3Schema;
|
|
201
|
+
const specSchema = schema?.properties?.spec;
|
|
202
|
+
const properties = specSchema ? extractSpecProperties(specSchema) : [];
|
|
203
|
+
const propertyTypes = specSchema ? extractPropertyTypes(specSchema, typeName) : [];
|
|
204
|
+
const enums = specSchema ? extractEnums(specSchema, typeName) : [];
|
|
205
|
+
|
|
206
|
+
results.push({
|
|
207
|
+
resource: {
|
|
208
|
+
typeName,
|
|
209
|
+
description: `Config Connector resource: ${spec.names.kind} (${spec.group})`,
|
|
210
|
+
properties,
|
|
211
|
+
attributes: [
|
|
212
|
+
{ name: "name", tsType: "string" },
|
|
213
|
+
{ name: "namespace", tsType: "string" },
|
|
214
|
+
{ name: "uid", tsType: "string" },
|
|
215
|
+
],
|
|
216
|
+
deprecatedProperties: [],
|
|
217
|
+
},
|
|
218
|
+
propertyTypes,
|
|
219
|
+
enums,
|
|
220
|
+
gvk,
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return results;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ── Property extraction ─────────────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Extract properties from the spec sub-object of a CRD schema.
|
|
230
|
+
*/
|
|
231
|
+
function extractSpecProperties(specSchema: OpenAPISchema): ParsedProperty[] {
|
|
232
|
+
const result: ParsedProperty[] = [];
|
|
233
|
+
const props = specSchema.properties ?? {};
|
|
234
|
+
const requiredSet = new Set<string>(specSchema.required ?? []);
|
|
235
|
+
|
|
236
|
+
for (const [name, prop] of Object.entries(props)) {
|
|
237
|
+
const isRef = isResourceRef(prop);
|
|
238
|
+
|
|
239
|
+
result.push({
|
|
240
|
+
name,
|
|
241
|
+
tsType: resolveSchemaType(prop),
|
|
242
|
+
required: requiredSet.has(name),
|
|
243
|
+
description: prop.description,
|
|
244
|
+
enum: prop.enum,
|
|
245
|
+
constraints: extractConstraints(prop),
|
|
246
|
+
...(isRef && { isResourceRef: true }),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Detect Config Connector resourceRef fields.
|
|
255
|
+
* These typically have `name` and `external` sub-properties,
|
|
256
|
+
* optionally with `namespace`, `kind`, and `apiVersion`.
|
|
257
|
+
*/
|
|
258
|
+
function isResourceRef(schema: OpenAPISchema): boolean {
|
|
259
|
+
if (schema.type !== "object" || !schema.properties) return false;
|
|
260
|
+
const keys = new Set(Object.keys(schema.properties));
|
|
261
|
+
// Must have either `name` or `external`
|
|
262
|
+
if (!keys.has("name") && !keys.has("external")) return false;
|
|
263
|
+
// Must be a small object with only ref-like keys
|
|
264
|
+
const refKeys = new Set(["name", "external", "namespace", "kind", "apiVersion"]);
|
|
265
|
+
for (const key of keys) {
|
|
266
|
+
if (!refKeys.has(key)) return false;
|
|
267
|
+
}
|
|
268
|
+
return keys.size >= 2;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Extract nested object types as ParsedPropertyType entries.
|
|
273
|
+
*/
|
|
274
|
+
function extractPropertyTypes(specSchema: OpenAPISchema, parentTypeName: string): ParsedPropertyType[] {
|
|
275
|
+
const results: ParsedPropertyType[] = [];
|
|
276
|
+
const props = specSchema.properties ?? {};
|
|
277
|
+
|
|
278
|
+
for (const [name, prop] of Object.entries(props)) {
|
|
279
|
+
// Skip resourceRef fields — they are treated as references, not nested types
|
|
280
|
+
if (isResourceRef(prop)) continue;
|
|
281
|
+
|
|
282
|
+
// Inline object definitions
|
|
283
|
+
if (prop.type === "object" && prop.properties) {
|
|
284
|
+
const ptName = `${parentTypeName}::${pascalCase(name)}`;
|
|
285
|
+
const requiredSet = new Set<string>(prop.required ?? []);
|
|
286
|
+
|
|
287
|
+
results.push({
|
|
288
|
+
name: ptName,
|
|
289
|
+
specType: name,
|
|
290
|
+
properties: Object.entries(prop.properties).map(([pName, pSchema]) => ({
|
|
291
|
+
name: pName,
|
|
292
|
+
tsType: resolveSchemaType(pSchema),
|
|
293
|
+
required: requiredSet.has(pName),
|
|
294
|
+
description: pSchema.description,
|
|
295
|
+
enum: pSchema.enum,
|
|
296
|
+
constraints: extractConstraints(pSchema),
|
|
297
|
+
...(isResourceRef(pSchema) && { isResourceRef: true }),
|
|
298
|
+
})),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Recurse into nested objects
|
|
302
|
+
for (const [subName, subProp] of Object.entries(prop.properties)) {
|
|
303
|
+
if (subProp.type === "object" && subProp.properties && !isResourceRef(subProp)) {
|
|
304
|
+
const subPtName = `${ptName}::${pascalCase(subName)}`;
|
|
305
|
+
const subRequiredSet = new Set<string>(subProp.required ?? []);
|
|
306
|
+
|
|
307
|
+
results.push({
|
|
308
|
+
name: subPtName,
|
|
309
|
+
specType: subName,
|
|
310
|
+
properties: Object.entries(subProp.properties).map(([spName, spSchema]) => ({
|
|
311
|
+
name: spName,
|
|
312
|
+
tsType: resolveSchemaType(spSchema),
|
|
313
|
+
required: subRequiredSet.has(spName),
|
|
314
|
+
description: spSchema.description,
|
|
315
|
+
enum: spSchema.enum,
|
|
316
|
+
constraints: extractConstraints(spSchema),
|
|
317
|
+
})),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Array of objects
|
|
324
|
+
if (prop.type === "array" && prop.items?.type === "object" && prop.items.properties) {
|
|
325
|
+
if (isResourceRef(prop.items)) continue;
|
|
326
|
+
|
|
327
|
+
const itemSchema = prop.items;
|
|
328
|
+
const ptName = `${parentTypeName}::${pascalCase(singularize(name))}`;
|
|
329
|
+
const requiredSet = new Set<string>(itemSchema.required ?? []);
|
|
330
|
+
|
|
331
|
+
results.push({
|
|
332
|
+
name: ptName,
|
|
333
|
+
specType: name,
|
|
334
|
+
properties: Object.entries(itemSchema.properties!).map(([pName, pSchema]) => ({
|
|
335
|
+
name: pName,
|
|
336
|
+
tsType: resolveSchemaType(pSchema),
|
|
337
|
+
required: requiredSet.has(pName),
|
|
338
|
+
description: pSchema.description,
|
|
339
|
+
enum: pSchema.enum,
|
|
340
|
+
constraints: extractConstraints(pSchema),
|
|
341
|
+
...(isResourceRef(pSchema) && { isResourceRef: true }),
|
|
342
|
+
})),
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return results;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Extract enum types from the spec schema.
|
|
352
|
+
*/
|
|
353
|
+
function extractEnums(specSchema: OpenAPISchema, parentTypeName: string): ParsedEnum[] {
|
|
354
|
+
const results: ParsedEnum[] = [];
|
|
355
|
+
const props = specSchema.properties ?? {};
|
|
356
|
+
|
|
357
|
+
for (const [name, prop] of Object.entries(props)) {
|
|
358
|
+
if (prop.enum && prop.enum.length > 0) {
|
|
359
|
+
results.push({
|
|
360
|
+
name: `${parentTypeName}::${pascalCase(name)}`,
|
|
361
|
+
values: prop.enum,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return results;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ── Schema type resolution ──────────────────────────────────────────
|
|
370
|
+
|
|
371
|
+
function resolveSchemaType(schema: OpenAPISchema): string {
|
|
372
|
+
if (!schema) return "any";
|
|
373
|
+
|
|
374
|
+
if (schema["x-kubernetes-int-or-string"]) return "string | number";
|
|
375
|
+
if (schema["x-kubernetes-preserve-unknown-fields"]) return "Record<string, any>";
|
|
376
|
+
|
|
377
|
+
if (schema.enum && schema.enum.length > 0) {
|
|
378
|
+
return schema.enum.map((v) => JSON.stringify(v)).join(" | ");
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
switch (schema.type) {
|
|
382
|
+
case "string":
|
|
383
|
+
return "string";
|
|
384
|
+
case "integer":
|
|
385
|
+
case "number":
|
|
386
|
+
return "number";
|
|
387
|
+
case "boolean":
|
|
388
|
+
return "boolean";
|
|
389
|
+
case "array":
|
|
390
|
+
if (schema.items) {
|
|
391
|
+
const itemType = resolveSchemaType(schema.items);
|
|
392
|
+
if (itemType.includes(" | ")) return `(${itemType})[]`;
|
|
393
|
+
return `${itemType}[]`;
|
|
394
|
+
}
|
|
395
|
+
return "any[]";
|
|
396
|
+
case "object":
|
|
397
|
+
if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
|
|
398
|
+
const valueType = resolveSchemaType(schema.additionalProperties);
|
|
399
|
+
return `Record<string, ${valueType}>`;
|
|
400
|
+
}
|
|
401
|
+
if (schema.properties) return "Record<string, any>"; // will be a property type
|
|
402
|
+
return "Record<string, any>";
|
|
403
|
+
default:
|
|
404
|
+
return "any";
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function extractConstraints(schema: OpenAPISchema): PropertyConstraints {
|
|
409
|
+
const constraints: PropertyConstraints = {};
|
|
410
|
+
if (schema.minimum !== undefined) constraints.minimum = schema.minimum;
|
|
411
|
+
if (schema.maximum !== undefined) constraints.maximum = schema.maximum;
|
|
412
|
+
if (schema.minLength !== undefined) constraints.minLength = schema.minLength;
|
|
413
|
+
if (schema.maxLength !== undefined) constraints.maxLength = schema.maxLength;
|
|
414
|
+
if (schema.pattern !== undefined) constraints.pattern = schema.pattern;
|
|
415
|
+
return constraints;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ── Utilities ───────────────────────────────────────────────────────
|
|
419
|
+
|
|
420
|
+
function pascalCase(str: string): string {
|
|
421
|
+
return str
|
|
422
|
+
.split(/[-_.]/)
|
|
423
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
424
|
+
.join("");
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function singularize(str: string): string {
|
|
428
|
+
if (str.endsWith("ies")) return str.slice(0, -3) + "y";
|
|
429
|
+
if (str.endsWith("ses")) return str.slice(0, -2);
|
|
430
|
+
if (str.endsWith("s") && !str.endsWith("ss")) return str.slice(0, -1);
|
|
431
|
+
return str;
|
|
432
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
apiVersion: apiextensions.k8s.io/v1
|
|
2
|
+
kind: CustomResourceDefinition
|
|
3
|
+
metadata:
|
|
4
|
+
name: computeinstances.compute.cnrm.cloud.google.com
|
|
5
|
+
spec:
|
|
6
|
+
group: compute.cnrm.cloud.google.com
|
|
7
|
+
names:
|
|
8
|
+
kind: ComputeInstance
|
|
9
|
+
plural: computeinstances
|
|
10
|
+
singular: computeinstance
|
|
11
|
+
versions:
|
|
12
|
+
- name: v1beta1
|
|
13
|
+
served: true
|
|
14
|
+
storage: true
|
|
15
|
+
schema:
|
|
16
|
+
openAPIV3Schema:
|
|
17
|
+
type: object
|
|
18
|
+
properties:
|
|
19
|
+
apiVersion:
|
|
20
|
+
type: string
|
|
21
|
+
kind:
|
|
22
|
+
type: string
|
|
23
|
+
metadata:
|
|
24
|
+
type: object
|
|
25
|
+
spec:
|
|
26
|
+
type: object
|
|
27
|
+
required:
|
|
28
|
+
- machineType
|
|
29
|
+
- zone
|
|
30
|
+
properties:
|
|
31
|
+
machineType:
|
|
32
|
+
type: string
|
|
33
|
+
description: The machine type to create.
|
|
34
|
+
zone:
|
|
35
|
+
type: string
|
|
36
|
+
description: The zone that the machine should be created in.
|
|
37
|
+
canIpForward:
|
|
38
|
+
type: boolean
|
|
39
|
+
description: Whether to allow sending and receiving of packets with non-matching source or destination IPs.
|
|
40
|
+
deletionProtection:
|
|
41
|
+
type: boolean
|
|
42
|
+
description: Enable deletion protection on this instance.
|
|
43
|
+
description:
|
|
44
|
+
type: string
|
|
45
|
+
description: A brief description of this resource.
|
|
46
|
+
networkInterfaceRef:
|
|
47
|
+
type: object
|
|
48
|
+
properties:
|
|
49
|
+
name:
|
|
50
|
+
type: string
|
|
51
|
+
external:
|
|
52
|
+
type: string
|
|
53
|
+
namespace:
|
|
54
|
+
type: string
|
|
55
|
+
bootDisk:
|
|
56
|
+
type: object
|
|
57
|
+
properties:
|
|
58
|
+
autoDelete:
|
|
59
|
+
type: boolean
|
|
60
|
+
deviceName:
|
|
61
|
+
type: string
|
|
62
|
+
initializeParams:
|
|
63
|
+
type: object
|
|
64
|
+
properties:
|
|
65
|
+
size:
|
|
66
|
+
type: integer
|
|
67
|
+
type:
|
|
68
|
+
type: string
|
|
69
|
+
imageRef:
|
|
70
|
+
type: object
|
|
71
|
+
properties:
|
|
72
|
+
name:
|
|
73
|
+
type: string
|
|
74
|
+
external:
|
|
75
|
+
type: string
|
|
76
|
+
tags:
|
|
77
|
+
type: array
|
|
78
|
+
items:
|
|
79
|
+
type: string
|
|
80
|
+
labels:
|
|
81
|
+
type: object
|
|
82
|
+
additionalProperties:
|
|
83
|
+
type: string
|
|
84
|
+
resourceID:
|
|
85
|
+
type: string
|
|
86
|
+
description: Immutable. Optional. The name of the resource.
|
|
87
|
+
status:
|
|
88
|
+
type: object
|
|
89
|
+
properties:
|
|
90
|
+
instanceId:
|
|
91
|
+
type: string
|
|
92
|
+
selfLink:
|
|
93
|
+
type: string
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
apiVersion: apiextensions.k8s.io/v1
|
|
2
|
+
kind: CustomResourceDefinition
|
|
3
|
+
metadata:
|
|
4
|
+
name: iampolicymembers.iam.cnrm.cloud.google.com
|
|
5
|
+
spec:
|
|
6
|
+
group: iam.cnrm.cloud.google.com
|
|
7
|
+
names:
|
|
8
|
+
kind: IAMPolicyMember
|
|
9
|
+
plural: iampolicymembers
|
|
10
|
+
singular: iampolicymember
|
|
11
|
+
versions:
|
|
12
|
+
- name: v1beta1
|
|
13
|
+
served: true
|
|
14
|
+
storage: true
|
|
15
|
+
schema:
|
|
16
|
+
openAPIV3Schema:
|
|
17
|
+
type: object
|
|
18
|
+
properties:
|
|
19
|
+
apiVersion:
|
|
20
|
+
type: string
|
|
21
|
+
kind:
|
|
22
|
+
type: string
|
|
23
|
+
metadata:
|
|
24
|
+
type: object
|
|
25
|
+
spec:
|
|
26
|
+
type: object
|
|
27
|
+
required:
|
|
28
|
+
- member
|
|
29
|
+
- role
|
|
30
|
+
- resourceRef
|
|
31
|
+
properties:
|
|
32
|
+
member:
|
|
33
|
+
type: string
|
|
34
|
+
description: The member identity (e.g., user:email, serviceAccount:email).
|
|
35
|
+
role:
|
|
36
|
+
type: string
|
|
37
|
+
description: The IAM role to grant.
|
|
38
|
+
resourceRef:
|
|
39
|
+
type: object
|
|
40
|
+
properties:
|
|
41
|
+
apiVersion:
|
|
42
|
+
type: string
|
|
43
|
+
kind:
|
|
44
|
+
type: string
|
|
45
|
+
name:
|
|
46
|
+
type: string
|
|
47
|
+
external:
|
|
48
|
+
type: string
|
|
49
|
+
namespace:
|
|
50
|
+
type: string
|
|
51
|
+
condition:
|
|
52
|
+
type: object
|
|
53
|
+
properties:
|
|
54
|
+
title:
|
|
55
|
+
type: string
|
|
56
|
+
description:
|
|
57
|
+
type: string
|
|
58
|
+
expression:
|
|
59
|
+
type: string
|
|
60
|
+
status:
|
|
61
|
+
type: object
|
|
62
|
+
properties:
|
|
63
|
+
conditions:
|
|
64
|
+
type: array
|
|
65
|
+
items:
|
|
66
|
+
type: object
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
apiVersion: compute.cnrm.cloud.google.com/v1beta1
|
|
2
|
+
kind: ComputeInstance
|
|
3
|
+
metadata:
|
|
4
|
+
name: my-vm
|
|
5
|
+
annotations:
|
|
6
|
+
cnrm.cloud.google.com/project-id: my-project
|
|
7
|
+
spec:
|
|
8
|
+
machineType: e2-medium
|
|
9
|
+
zone: us-central1-a
|
|
10
|
+
bootDisk:
|
|
11
|
+
initializeParams:
|
|
12
|
+
sourceImageRef:
|
|
13
|
+
external: projects/debian-cloud/global/images/debian-11
|
|
14
|
+
networkInterface:
|
|
15
|
+
- networkRef:
|
|
16
|
+
name: my-network
|
|
17
|
+
subnetworkRef:
|
|
18
|
+
name: my-subnet
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
apiVersion: storage.cnrm.cloud.google.com/v1beta1
|
|
2
|
+
kind: StorageBucket
|
|
3
|
+
metadata:
|
|
4
|
+
name: app-assets
|
|
5
|
+
annotations:
|
|
6
|
+
cnrm.cloud.google.com/project-id: my-project
|
|
7
|
+
spec:
|
|
8
|
+
location: US
|
|
9
|
+
storageClass: STANDARD
|
|
10
|
+
uniformBucketLevelAccess: true
|
|
11
|
+
---
|
|
12
|
+
apiVersion: iam.cnrm.cloud.google.com/v1beta1
|
|
13
|
+
kind: IAMPolicyMember
|
|
14
|
+
metadata:
|
|
15
|
+
name: app-sa-bucket-reader
|
|
16
|
+
annotations:
|
|
17
|
+
cnrm.cloud.google.com/project-id: my-project
|
|
18
|
+
spec:
|
|
19
|
+
member: serviceAccount:app@my-project.iam.gserviceaccount.com
|
|
20
|
+
role: roles/storage.objectViewer
|
|
21
|
+
resourceRef:
|
|
22
|
+
apiVersion: storage.cnrm.cloud.google.com/v1beta1
|
|
23
|
+
kind: StorageBucket
|
|
24
|
+
name: app-assets
|
|
25
|
+
---
|
|
26
|
+
apiVersion: compute.cnrm.cloud.google.com/v1beta1
|
|
27
|
+
kind: ComputeNetwork
|
|
28
|
+
metadata:
|
|
29
|
+
name: app-network
|
|
30
|
+
annotations:
|
|
31
|
+
cnrm.cloud.google.com/project-id: my-project
|
|
32
|
+
spec:
|
|
33
|
+
autoCreateSubnetworks: false
|
|
34
|
+
routingMode: REGIONAL
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
apiVersion: storage.cnrm.cloud.google.com/v1beta1
|
|
2
|
+
kind: StorageBucket
|
|
3
|
+
metadata:
|
|
4
|
+
name: my-data-bucket
|
|
5
|
+
annotations:
|
|
6
|
+
cnrm.cloud.google.com/project-id: my-project
|
|
7
|
+
spec:
|
|
8
|
+
location: US
|
|
9
|
+
storageClass: STANDARD
|
|
10
|
+
uniformBucketLevelAccess: true
|
|
11
|
+
versioning:
|
|
12
|
+
enabled: true
|