@intentius/chant-lexicon-gcp 0.1.0 → 0.1.5
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 +37 -37
- package/dist/manifest.json +1 -1
- package/package.json +7 -7
- package/src/codegen/docs-cli.ts +1 -1
- package/src/codegen/docs.test.ts +1 -1
- package/src/codegen/generate-cli.ts +1 -1
- package/src/codegen/generate.test.ts +1 -1
- package/src/codegen/naming.test.ts +1 -1
- package/src/codegen/package.test.ts +1 -1
- package/src/codegen/package.ts +2 -7
- package/src/composites/composites.test.ts +1 -1
- package/src/composites/gke-crdb-region.ts +232 -0
- package/src/composites/index.ts +4 -0
- package/src/composites/multi-region-vpc.ts +157 -0
- package/src/coverage.test.ts +1 -1
- package/src/default-labels.test.ts +1 -1
- package/src/import/generator.test.ts +1 -1
- package/src/import/import-fixtures.test.ts +1 -1
- package/src/import/parser.test.ts +1 -1
- package/src/import/roundtrip.test.ts +1 -1
- package/src/index.ts +1 -1
- package/src/lint/post-synth/gcp-helpers.test.ts +1 -1
- package/src/lint/post-synth/post-synth.test.ts +1 -1
- package/src/lint/post-synth/wgc101.test.ts +1 -1
- package/src/lint/post-synth/wgc102.test.ts +1 -1
- package/src/lint/post-synth/wgc103.test.ts +1 -1
- package/src/lint/post-synth/wgc104.test.ts +1 -1
- package/src/lint/post-synth/wgc105.test.ts +1 -1
- package/src/lint/post-synth/wgc106.test.ts +1 -1
- package/src/lint/post-synth/wgc107.test.ts +1 -1
- package/src/lint/post-synth/wgc108.test.ts +1 -1
- package/src/lint/post-synth/wgc109.test.ts +1 -1
- package/src/lint/post-synth/wgc110.test.ts +1 -1
- package/src/lint/post-synth/wgc111.test.ts +1 -1
- package/src/lint/post-synth/wgc112.test.ts +1 -1
- package/src/lint/post-synth/wgc113.test.ts +1 -1
- package/src/lint/post-synth/wgc201.test.ts +1 -1
- package/src/lint/post-synth/wgc202.test.ts +1 -1
- package/src/lint/post-synth/wgc203.test.ts +1 -1
- package/src/lint/post-synth/wgc204.test.ts +1 -1
- package/src/lint/post-synth/wgc301.test.ts +1 -1
- package/src/lint/post-synth/wgc302.test.ts +1 -1
- package/src/lint/post-synth/wgc303.test.ts +1 -1
- package/src/lint/post-synth/wgc401.test.ts +1 -1
- package/src/lint/post-synth/wgc402.test.ts +1 -1
- package/src/lint/post-synth/wgc403.test.ts +1 -1
- package/src/lint/rules/rules.test.ts +1 -1
- package/src/lsp/completions.test.ts +1 -1
- package/src/lsp/hover.test.ts +1 -1
- package/src/package-cli.ts +2 -2
- package/src/plugin.test.ts +1 -1
- package/src/plugin.ts +7 -9
- package/src/pseudo.test.ts +1 -1
- package/src/serializer.test.ts +1 -1
- package/src/spec/fetch.test.ts +2 -2
- package/src/spec/parse.test.ts +1 -1
- package/src/validate-cli.ts +1 -1
- package/src/validate.test.ts +1 -1
package/dist/integrity.json
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
{
|
|
2
|
-
"algorithm": "
|
|
2
|
+
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
5
|
-
"meta.json": "
|
|
6
|
-
"types/index.d.ts": "
|
|
7
|
-
"rules/hardcoded-
|
|
8
|
-
"rules/hardcoded-
|
|
9
|
-
"rules/public-iam.ts": "
|
|
10
|
-
"rules/
|
|
11
|
-
"rules/
|
|
12
|
-
"rules/
|
|
13
|
-
"rules/
|
|
14
|
-
"rules/
|
|
15
|
-
"rules/
|
|
16
|
-
"rules/
|
|
17
|
-
"rules/
|
|
18
|
-
"rules/
|
|
19
|
-
"rules/wgc108.ts": "
|
|
20
|
-
"rules/
|
|
21
|
-
"rules/
|
|
22
|
-
"rules/
|
|
23
|
-
"rules/
|
|
24
|
-
"rules/
|
|
25
|
-
"rules/
|
|
26
|
-
"rules/
|
|
27
|
-
"rules/
|
|
28
|
-
"rules/
|
|
29
|
-
"rules/
|
|
30
|
-
"rules/
|
|
31
|
-
"rules/
|
|
32
|
-
"rules/
|
|
33
|
-
"rules/
|
|
34
|
-
"rules/
|
|
35
|
-
"skills/chant-gcp.md": "
|
|
36
|
-
"skills/chant-gcp-security.md": "
|
|
37
|
-
"skills/chant-gcp-patterns.md": "
|
|
38
|
-
"skills/chant-gcp-gke.md": "
|
|
4
|
+
"manifest.json": "e69237da96eb541c1612bd2c6ce49afe19ef875c3a9958cb973de69487b505b7",
|
|
5
|
+
"meta.json": "2bc3713e9e01e90832d18dedaf4bf544dd4d8fe32f1363f7e95dc711d3d76e9d",
|
|
6
|
+
"types/index.d.ts": "0554f7e883c6216ba735cbe79a8534ccad762bea28cc4d4c4884f8760a2f1dd3",
|
|
7
|
+
"rules/hardcoded-project.ts": "228631d3159e1ffcce2359c66073d4ae59bb0285f378e46e446f416aec50481c",
|
|
8
|
+
"rules/hardcoded-region.ts": "9135e337202d44787e93243811a942f2209627ce522f27a6f3c7c0ad0e2e3545",
|
|
9
|
+
"rules/public-iam.ts": "bf2fe502ad5bfb077bdb3039a6bb228a3214cff2769996d65362bc3f7c5bb730",
|
|
10
|
+
"rules/gcp-helpers.ts": "0420adb73527b29a288e219b24742f4f29ef83859228d61fdaff83b1fb800bf1",
|
|
11
|
+
"rules/schema-registry.ts": "825862c574f0291b07f46478018874556a190940a1653b24e9dec9f1427d8d8b",
|
|
12
|
+
"rules/wgc101.ts": "679f245ea4665d687848382ff1b934ae133a3c320cc051011cc4d95802e66b38",
|
|
13
|
+
"rules/wgc102.ts": "22cb997d40a59ce3e0ff833ce946a5571e13d4811b255d4b67db570d58575e07",
|
|
14
|
+
"rules/wgc103.ts": "589741dbe5828508b5424a74f63f0202ba8452237c366173af283073f81e0317",
|
|
15
|
+
"rules/wgc104.ts": "5e1a74ffae157296de4882e4d0693146a24d43f3f59f5db80f4b0faf2da230a3",
|
|
16
|
+
"rules/wgc105.ts": "7208632ce9bc264c597453f1bd5e62f3569207e4e2f988ebb8f0451f3d714184",
|
|
17
|
+
"rules/wgc106.ts": "916c199b2b1ce064f9e15bcdd0a052e9eb64cb79eb79d03aeae58708b1e894e3",
|
|
18
|
+
"rules/wgc107.ts": "022474be5f9150c6f352ebbfa5ee9bbe37d2389bfc23bd091c5bedaa7f28a7c3",
|
|
19
|
+
"rules/wgc108.ts": "170db62ee6af7d5c9f25e1bde9fb308e074178192291992360a77407edec7486",
|
|
20
|
+
"rules/wgc109.ts": "468b889c6d2bf804e0e214daa0007b5a2f9a8c0d614b57134ac5be9739b7a2a2",
|
|
21
|
+
"rules/wgc110.ts": "632f692c6e67efad14e24edd1f1cb41d11faefa8d77efcd4e4af93ff2605be3a",
|
|
22
|
+
"rules/wgc111.ts": "7bb8d5c4121d4038b2089728f3c8ae7f5306163101d157ceccc78a982a9be872",
|
|
23
|
+
"rules/wgc112.ts": "d6ff610929ee9c85b0f3c455de83ab1c1f984a5c6f9a0b2989ba2cd50e74e4f8",
|
|
24
|
+
"rules/wgc113.ts": "59d40d6f433b2e448ed895775d3dbbcfad22c3ac41b08c7a32d49474a3973c2a",
|
|
25
|
+
"rules/wgc201.ts": "96a4ab92c4d7f0ed4f33dc44b7ab64b5ec4ccdd48f24fd69374be79e33769781",
|
|
26
|
+
"rules/wgc202.ts": "44e4231a9e6ed7e5917506d8d60554cf0ec0e64f148ba7a7ad739cb8bb9e51a0",
|
|
27
|
+
"rules/wgc203.ts": "716b45cb305634927840623a5dfd8b6c0375420be25096902ab1e5bd0ff71671",
|
|
28
|
+
"rules/wgc204.ts": "6a0fea0a0e002065fbb5ce11f29dea183b98ec70134987fa1889731235ab4f70",
|
|
29
|
+
"rules/wgc301.ts": "a7b1ec58d4582911bfef7f82fd83a97706525c2173e2843958f2e62cdaa3fc2a",
|
|
30
|
+
"rules/wgc302.ts": "94423099c34e1c2193966920a441cb7af80b1048720bdca1263f444dc666b941",
|
|
31
|
+
"rules/wgc303.ts": "70cdf211020b6bcf3d62d46368d3e753911700ec76478d4a2bb788f75d608c20",
|
|
32
|
+
"rules/wgc401.ts": "287a28258a6987eacf4228df8e57cdc6d5357d5efcb35a732fb01e4b7b8ce797",
|
|
33
|
+
"rules/wgc402.ts": "4dacefea0ead25987ded5f8f5b83d02d2aa957f3816a7d0cc602cc9872257458",
|
|
34
|
+
"rules/wgc403.ts": "156a8af8f8e419d36e740f4fc6f2ed7576a1692e3286e4c9ca6f9e9ccf772965",
|
|
35
|
+
"skills/chant-gcp.md": "6fd732fb5df2fd6561b4b704379f6779ff4ac12e3069ea4f641d9b222e0c3c81",
|
|
36
|
+
"skills/chant-gcp-security.md": "39a1c97b7425ed43b081257132449d11ce9d12f2d4755fc30d8b14bbc38f60bd",
|
|
37
|
+
"skills/chant-gcp-patterns.md": "a7ef31c1eb2f7244d3f73952c300472ef94c1eb09bd7a1003281b89299b6b704",
|
|
38
|
+
"skills/chant-gcp-gke.md": "be277019da9a722c851e47cd2dfb9c9536668948c3535fb20db7697e934c4e2b"
|
|
39
39
|
},
|
|
40
|
-
"composite": "
|
|
40
|
+
"composite": "14ab4b8a9a33f2ee5438cc51772c626faab0418114bad62c3cb89f746186232f"
|
|
41
41
|
}
|
package/dist/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-gcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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",
|
|
@@ -36,18 +36,18 @@
|
|
|
36
36
|
"./types": "./dist/types/index.d.ts"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
|
-
"generate": "
|
|
40
|
-
"bundle": "
|
|
41
|
-
"validate": "
|
|
42
|
-
"docs": "
|
|
43
|
-
"prepack": "
|
|
39
|
+
"generate": "tsx src/codegen/generate-cli.ts",
|
|
40
|
+
"bundle": "tsx src/package-cli.ts",
|
|
41
|
+
"validate": "tsx src/validate-cli.ts",
|
|
42
|
+
"docs": "tsx src/codegen/docs-cli.ts",
|
|
43
|
+
"prepack": "npm run generate && npm run bundle && npm run validate"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"fflate": "^0.8.2",
|
|
47
47
|
"js-yaml": "^4.1.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@intentius/chant": "
|
|
50
|
+
"@intentius/chant": "*",
|
|
51
51
|
"typescript": "^5.9.3"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
package/src/codegen/docs-cli.ts
CHANGED
package/src/codegen/docs.test.ts
CHANGED
package/src/codegen/package.ts
CHANGED
|
@@ -3,9 +3,8 @@
|
|
|
3
3
|
* with GCP-specific manifest building and skill collection.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createRequire } from "module";
|
|
7
6
|
import { readFileSync } from "fs";
|
|
8
|
-
|
|
7
|
+
import { gcpPlugin } from "../plugin";
|
|
9
8
|
import { dirname } from "path";
|
|
10
9
|
import { fileURLToPath } from "url";
|
|
11
10
|
import {
|
|
@@ -32,8 +31,6 @@ export async function packageLexicon(opts: PackageOptions = {}): Promise<Package
|
|
|
32
31
|
generate: (genOpts) => generate({ verbose: genOpts.verbose, force: genOpts.force }),
|
|
33
32
|
|
|
34
33
|
buildManifest: (_genResult) => {
|
|
35
|
-
const { gcpPlugin } = require("../plugin");
|
|
36
|
-
|
|
37
34
|
const pseudoParams: string[] = gcpPlugin.pseudoParameters?.() ?? [];
|
|
38
35
|
const pseudoParameters: Record<string, string> = {};
|
|
39
36
|
for (const p of pseudoParams) {
|
|
@@ -53,9 +50,7 @@ export async function packageLexicon(opts: PackageOptions = {}): Promise<Package
|
|
|
53
50
|
|
|
54
51
|
srcDir: pkgDir,
|
|
55
52
|
|
|
56
|
-
collectSkills: () => {
|
|
57
|
-
const { gcpPlugin } = require("../plugin");
|
|
58
|
-
const skillDefs = gcpPlugin.skills?.() ?? [];
|
|
53
|
+
collectSkills: () => { const skillDefs = gcpPlugin.skills?.() ?? [];
|
|
59
54
|
return collectSkills(skillDefs);
|
|
60
55
|
},
|
|
61
56
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, test, expect } from "
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { GkeCluster } from "./gke-cluster";
|
|
3
3
|
import { CloudRunServiceComposite } from "./cloud-run-service";
|
|
4
4
|
import { CloudSqlInstance } from "./cloud-sql-instance";
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GkeCrdbRegion composite — GKE cluster + public DNS zone + per-region IAM for a CockroachDB region.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the 2-file per-region GCP infra pattern (infra/cluster.ts + infra/dns.ts) with
|
|
5
|
+
* a single composite call. Handles the GKE cluster, the public DNS zone, and all per-region
|
|
6
|
+
* Workload Identity IAM bindings for both ExternalDNS and CockroachDB pods.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Composite } from "@intentius/chant";
|
|
10
|
+
import { GkeCluster } from "./gke-cluster";
|
|
11
|
+
import { DNSManagedZone, GCPServiceAccount, IAMPolicyMember } from "../generated";
|
|
12
|
+
|
|
13
|
+
export interface GkeCrdbRegionNodeConfig {
|
|
14
|
+
/** Machine type (default: "n2-standard-2"). */
|
|
15
|
+
machineType?: string;
|
|
16
|
+
/** Disk size per node in GB (default: 100). */
|
|
17
|
+
diskSizeGb?: number;
|
|
18
|
+
/** Initial/min node count (default: 1). */
|
|
19
|
+
nodeCount?: number;
|
|
20
|
+
/** Maximum node count for autoscaling (default: 3). */
|
|
21
|
+
maxNodeCount?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface GkeCrdbRegionConfig {
|
|
25
|
+
/**
|
|
26
|
+
* GCP region (e.g. "us-east4").
|
|
27
|
+
* Used as both the cluster location and for NAT router naming.
|
|
28
|
+
*/
|
|
29
|
+
region: string;
|
|
30
|
+
/** GKE cluster name (e.g. "gke-crdb-east"). Used as prefix for child resources. */
|
|
31
|
+
clusterName: string;
|
|
32
|
+
/** VPC network resource name. */
|
|
33
|
+
network: string;
|
|
34
|
+
/** Node subnet resource name for the cluster. */
|
|
35
|
+
subnetwork: string;
|
|
36
|
+
/** Public DNS domain for this region (e.g. "east.crdb.example.com"). */
|
|
37
|
+
domain: string;
|
|
38
|
+
/** GCP project ID for Workload Identity pool and service account names. */
|
|
39
|
+
project: string;
|
|
40
|
+
/**
|
|
41
|
+
* K8s namespace where CockroachDB pods run (e.g. "crdb-east").
|
|
42
|
+
* Used in the Workload Identity binding subject for CRDB pods.
|
|
43
|
+
*/
|
|
44
|
+
crdbNamespace: string;
|
|
45
|
+
/**
|
|
46
|
+
* K8s ServiceAccount name for CockroachDB pods (default: "cockroachdb").
|
|
47
|
+
* Used in the Workload Identity binding subject.
|
|
48
|
+
*/
|
|
49
|
+
crdbK8sSa?: string;
|
|
50
|
+
/**
|
|
51
|
+
* CIDR block for the GKE master's private endpoint (e.g. "172.16.0.0/28").
|
|
52
|
+
* Must be /28, unique per cluster, not overlapping with node/pod CIDRs.
|
|
53
|
+
*/
|
|
54
|
+
masterCidr?: string;
|
|
55
|
+
/** Node pool configuration. */
|
|
56
|
+
nodeConfig?: GkeCrdbRegionNodeConfig;
|
|
57
|
+
/** GCP release channel for cluster upgrades (default: "REGULAR"). */
|
|
58
|
+
releaseChannel?: "RAPID" | "REGULAR" | "STABLE";
|
|
59
|
+
/** Optional backup bucket name to grant CRDB GSA storage.objectAdmin access. */
|
|
60
|
+
backupBucket?: string;
|
|
61
|
+
/** Additional labels for all resources. */
|
|
62
|
+
labels?: Record<string, string>;
|
|
63
|
+
/** Namespace for Config Connector resources. */
|
|
64
|
+
namespace?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create a GkeCrdbRegion composite — returns all GCP resources for one CockroachDB region:
|
|
69
|
+
* GKE cluster, public DNS zone, per-region GSAs, and Workload Identity IAM bindings.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { GkeCrdbRegion } from "@intentius/chant-lexicon-gcp";
|
|
74
|
+
*
|
|
75
|
+
* export const east = GkeCrdbRegion({
|
|
76
|
+
* region: "us-east4",
|
|
77
|
+
* clusterName: "gke-crdb-east",
|
|
78
|
+
* network: "crdb-multi-region",
|
|
79
|
+
* subnetwork: "crdb-multi-region-east-nodes",
|
|
80
|
+
* domain: "east.crdb.example.com",
|
|
81
|
+
* project: GCP_PROJECT_ID,
|
|
82
|
+
* crdbNamespace: "crdb-east",
|
|
83
|
+
* masterCidr: "172.16.0.0/28",
|
|
84
|
+
* });
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export const GkeCrdbRegion = Composite<GkeCrdbRegionConfig>((props) => {
|
|
88
|
+
const {
|
|
89
|
+
region,
|
|
90
|
+
clusterName,
|
|
91
|
+
network,
|
|
92
|
+
subnetwork,
|
|
93
|
+
domain,
|
|
94
|
+
project,
|
|
95
|
+
crdbNamespace,
|
|
96
|
+
crdbK8sSa = "cockroachdb",
|
|
97
|
+
masterCidr = "172.16.0.0/28",
|
|
98
|
+
nodeConfig = {},
|
|
99
|
+
releaseChannel = "REGULAR",
|
|
100
|
+
backupBucket,
|
|
101
|
+
labels: extraLabels = {},
|
|
102
|
+
namespace,
|
|
103
|
+
} = props;
|
|
104
|
+
|
|
105
|
+
const {
|
|
106
|
+
machineType = "n2-standard-2",
|
|
107
|
+
diskSizeGb = 100,
|
|
108
|
+
nodeCount = 1,
|
|
109
|
+
maxNodeCount = 3,
|
|
110
|
+
} = nodeConfig;
|
|
111
|
+
|
|
112
|
+
const commonLabels: Record<string, string> = {
|
|
113
|
+
"app.kubernetes.io/name": clusterName,
|
|
114
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
115
|
+
...extraLabels,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const meta = (component: string, resourceName: string) => ({
|
|
119
|
+
name: resourceName,
|
|
120
|
+
...(namespace && { namespace }),
|
|
121
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": component },
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// ── GKE Cluster ──────────────────────────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
const { cluster, nodePool, defaultPool } = GkeCluster({
|
|
127
|
+
name: clusterName,
|
|
128
|
+
location: region,
|
|
129
|
+
machineType,
|
|
130
|
+
diskSizeGb,
|
|
131
|
+
initialNodeCount: nodeCount,
|
|
132
|
+
minNodeCount: nodeCount,
|
|
133
|
+
maxNodeCount,
|
|
134
|
+
network,
|
|
135
|
+
subnetwork,
|
|
136
|
+
workloadIdentity: true,
|
|
137
|
+
privateNodes: true,
|
|
138
|
+
masterCidr,
|
|
139
|
+
releaseChannel,
|
|
140
|
+
labels: extraLabels,
|
|
141
|
+
...(namespace && { namespace }),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// ── Public DNS Zone ───────────────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
const dnsZoneName = `${clusterName}-zone`;
|
|
147
|
+
|
|
148
|
+
const dnsZone = new DNSManagedZone({
|
|
149
|
+
metadata: meta("dns", dnsZoneName),
|
|
150
|
+
dnsName: `${domain}.`,
|
|
151
|
+
description: `CockroachDB ${region} UI — managed by chant`,
|
|
152
|
+
} as Record<string, unknown>);
|
|
153
|
+
|
|
154
|
+
// ── ExternalDNS Service Account ───────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
const dnsGsaName = `${clusterName}-dns`;
|
|
157
|
+
|
|
158
|
+
const dnsGsa = new GCPServiceAccount({
|
|
159
|
+
metadata: meta("iam", dnsGsaName),
|
|
160
|
+
displayName: `CockroachDB ${region} ExternalDNS workload identity`,
|
|
161
|
+
} as Record<string, unknown>);
|
|
162
|
+
|
|
163
|
+
const dnsWiBinding = new IAMPolicyMember({
|
|
164
|
+
metadata: meta("iam", `${dnsGsaName}-wi`),
|
|
165
|
+
member: `serviceAccount:${project}.svc.id.goog[kube-system/external-dns-sa]`,
|
|
166
|
+
role: "roles/iam.workloadIdentityUser",
|
|
167
|
+
resourceRef: {
|
|
168
|
+
apiVersion: "iam.cnrm.cloud.google.com/v1beta1",
|
|
169
|
+
kind: "IAMServiceAccount",
|
|
170
|
+
name: dnsGsaName,
|
|
171
|
+
},
|
|
172
|
+
} as Record<string, unknown>);
|
|
173
|
+
|
|
174
|
+
const dnsAdminBinding = new IAMPolicyMember({
|
|
175
|
+
metadata: meta("iam", `${dnsGsaName}-admin`),
|
|
176
|
+
member: `serviceAccount:${dnsGsaName}@${project}.iam.gserviceaccount.com`,
|
|
177
|
+
role: "roles/dns.admin",
|
|
178
|
+
resourceRef: {
|
|
179
|
+
apiVersion: "resourcemanager.cnrm.cloud.google.com/v1beta1",
|
|
180
|
+
kind: "Project",
|
|
181
|
+
external: `projects/${project}`,
|
|
182
|
+
},
|
|
183
|
+
} as Record<string, unknown>);
|
|
184
|
+
|
|
185
|
+
// ── CockroachDB Service Account ───────────────────────────────────────────────
|
|
186
|
+
|
|
187
|
+
const crdbGsaName = `${clusterName}-crdb`;
|
|
188
|
+
|
|
189
|
+
const crdbGsa = new GCPServiceAccount({
|
|
190
|
+
metadata: meta("iam", crdbGsaName),
|
|
191
|
+
displayName: `CockroachDB ${region} workload identity`,
|
|
192
|
+
} as Record<string, unknown>);
|
|
193
|
+
|
|
194
|
+
const crdbWiBinding = new IAMPolicyMember({
|
|
195
|
+
metadata: meta("iam", `${crdbGsaName}-wi`),
|
|
196
|
+
member: `serviceAccount:${project}.svc.id.goog[${crdbNamespace}/${crdbK8sSa}]`,
|
|
197
|
+
role: "roles/iam.workloadIdentityUser",
|
|
198
|
+
resourceRef: {
|
|
199
|
+
apiVersion: "iam.cnrm.cloud.google.com/v1beta1",
|
|
200
|
+
kind: "IAMServiceAccount",
|
|
201
|
+
name: crdbGsaName,
|
|
202
|
+
},
|
|
203
|
+
} as Record<string, unknown>);
|
|
204
|
+
|
|
205
|
+
const result: Record<string, any> = {
|
|
206
|
+
cluster,
|
|
207
|
+
nodePool,
|
|
208
|
+
defaultPool,
|
|
209
|
+
dnsZone,
|
|
210
|
+
dnsGsa,
|
|
211
|
+
dnsWiBinding,
|
|
212
|
+
dnsAdminBinding,
|
|
213
|
+
crdbGsa,
|
|
214
|
+
crdbWiBinding,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Optional: GCS backup access for CRDB GSA
|
|
218
|
+
if (backupBucket) {
|
|
219
|
+
result.crdbBackupBinding = new IAMPolicyMember({
|
|
220
|
+
metadata: meta("iam", `${crdbGsaName}-backup`),
|
|
221
|
+
member: `serviceAccount:${crdbGsaName}@${project}.iam.gserviceaccount.com`,
|
|
222
|
+
role: "roles/storage.objectAdmin",
|
|
223
|
+
resourceRef: {
|
|
224
|
+
apiVersion: "storage.cnrm.cloud.google.com/v1beta1",
|
|
225
|
+
kind: "StorageBucket",
|
|
226
|
+
name: backupBucket,
|
|
227
|
+
},
|
|
228
|
+
} as Record<string, unknown>);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return result;
|
|
232
|
+
}, "GkeCrdbRegion");
|
package/src/composites/index.ts
CHANGED
|
@@ -20,3 +20,7 @@ export { SecureProject } from "./secure-project";
|
|
|
20
20
|
export type { SecureProjectProps } from "./secure-project";
|
|
21
21
|
export { MemorystoreRedis } from "./memorystore-redis";
|
|
22
22
|
export type { MemorystoreRedisProps } from "./memorystore-redis";
|
|
23
|
+
export { MultiRegionVpc } from "./multi-region-vpc";
|
|
24
|
+
export type { MultiRegionVpcConfig, MultiRegionVpcRegion } from "./multi-region-vpc";
|
|
25
|
+
export { GkeCrdbRegion } from "./gke-crdb-region";
|
|
26
|
+
export type { GkeCrdbRegionConfig, GkeCrdbRegionNodeConfig } from "./gke-crdb-region";
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MultiRegionVpc composite — VPC + 2 subnets per region + Router + RouterNAT per region + Firewall.
|
|
3
|
+
*
|
|
4
|
+
* Collapses the boilerplate of a multi-region GKE networking stack into a single call.
|
|
5
|
+
* Each region gets node and pod subnets, a Cloud Router, and a Cloud NAT gateway.
|
|
6
|
+
* A single allow-internal firewall rule covers all region CIDRs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Composite } from "@intentius/chant";
|
|
10
|
+
import { VPCNetwork, Subnetwork, Router, RouterNAT, Firewall } from "../generated";
|
|
11
|
+
|
|
12
|
+
export interface MultiRegionVpcRegion {
|
|
13
|
+
/** GCP region name (e.g. "us-east4"). Used as the Router/NAT region. */
|
|
14
|
+
region: string;
|
|
15
|
+
/**
|
|
16
|
+
* Short alias used in resource names (e.g. "east").
|
|
17
|
+
* Defaults to the full region string. Use this to keep names concise.
|
|
18
|
+
*/
|
|
19
|
+
regionAlias?: string;
|
|
20
|
+
/** IP CIDR for GKE node subnet (e.g. "10.1.0.0/20"). */
|
|
21
|
+
nodeSubnetCidr: string;
|
|
22
|
+
/** IP CIDR for GKE pod subnet (e.g. "10.1.16.0/20"). */
|
|
23
|
+
podSubnetCidr: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface MultiRegionVpcConfig {
|
|
27
|
+
/** VPC network name. Used as prefix for all sub-resources. */
|
|
28
|
+
name: string;
|
|
29
|
+
/** One entry per GCP region. */
|
|
30
|
+
regions: MultiRegionVpcRegion[];
|
|
31
|
+
/** Enable VPC flow logs on all subnets (default: false). */
|
|
32
|
+
enableFlowLogs?: boolean;
|
|
33
|
+
/** Additional labels for all resources. */
|
|
34
|
+
labels?: Record<string, string>;
|
|
35
|
+
/** Namespace for Config Connector resources. */
|
|
36
|
+
namespace?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a MultiRegionVpc composite — one VPC with subnets, Cloud NAT, and an
|
|
41
|
+
* allow-internal firewall for every region in the array.
|
|
42
|
+
*
|
|
43
|
+
* The composite eliminates the per-region Router/NAT boilerplate that arises when
|
|
44
|
+
* `VpcNetwork` only handles a single NAT region and the rest must be wired manually.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* import { MultiRegionVpc } from "@intentius/chant-lexicon-gcp";
|
|
49
|
+
*
|
|
50
|
+
* export const network = MultiRegionVpc({
|
|
51
|
+
* name: "crdb-multi-region",
|
|
52
|
+
* regions: [
|
|
53
|
+
* { region: "us-east4", regionAlias: "east", nodeSubnetCidr: "10.1.0.0/20", podSubnetCidr: "10.1.16.0/20" },
|
|
54
|
+
* { region: "us-central1", regionAlias: "central", nodeSubnetCidr: "10.2.0.0/20", podSubnetCidr: "10.2.16.0/20" },
|
|
55
|
+
* { region: "us-west1", regionAlias: "west", nodeSubnetCidr: "10.3.0.0/20", podSubnetCidr: "10.3.16.0/20" },
|
|
56
|
+
* ],
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export const MultiRegionVpc = Composite<MultiRegionVpcConfig>((props) => {
|
|
61
|
+
const {
|
|
62
|
+
name,
|
|
63
|
+
regions,
|
|
64
|
+
enableFlowLogs = false,
|
|
65
|
+
labels: extraLabels = {},
|
|
66
|
+
namespace,
|
|
67
|
+
} = props;
|
|
68
|
+
|
|
69
|
+
const commonLabels: Record<string, string> = {
|
|
70
|
+
"app.kubernetes.io/name": name,
|
|
71
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
72
|
+
...extraLabels,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const meta = (component: string, resourceName?: string) => ({
|
|
76
|
+
metadata: {
|
|
77
|
+
name: resourceName ?? name,
|
|
78
|
+
...(namespace && { namespace }),
|
|
79
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": component },
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const network = new VPCNetwork({
|
|
84
|
+
...meta("network"),
|
|
85
|
+
autoCreateSubnetworks: false,
|
|
86
|
+
routingMode: "REGIONAL",
|
|
87
|
+
} as Record<string, unknown>);
|
|
88
|
+
|
|
89
|
+
const subnetEntries: Record<string, unknown> = {};
|
|
90
|
+
const routerEntries: Record<string, unknown> = {};
|
|
91
|
+
const natEntries: Record<string, unknown> = {};
|
|
92
|
+
const allCidrs: string[] = [];
|
|
93
|
+
|
|
94
|
+
for (const r of regions) {
|
|
95
|
+
const alias = r.regionAlias ?? r.region;
|
|
96
|
+
|
|
97
|
+
allCidrs.push(r.nodeSubnetCidr, r.podSubnetCidr);
|
|
98
|
+
|
|
99
|
+
const flowConfig = enableFlowLogs
|
|
100
|
+
? { logConfig: { enable: true, aggregationInterval: "INTERVAL_5_SEC", flowSampling: 0.5 } }
|
|
101
|
+
: {};
|
|
102
|
+
|
|
103
|
+
subnetEntries[`subnet_${alias}_nodes`] = new Subnetwork({
|
|
104
|
+
...meta("subnet", `${name}-${alias}-nodes`),
|
|
105
|
+
networkRef: { name },
|
|
106
|
+
ipCidrRange: r.nodeSubnetCidr,
|
|
107
|
+
region: r.region,
|
|
108
|
+
privateIpGoogleAccess: true,
|
|
109
|
+
...flowConfig,
|
|
110
|
+
} as Record<string, unknown>);
|
|
111
|
+
|
|
112
|
+
subnetEntries[`subnet_${alias}_pods`] = new Subnetwork({
|
|
113
|
+
...meta("subnet", `${name}-${alias}-pods`),
|
|
114
|
+
networkRef: { name },
|
|
115
|
+
ipCidrRange: r.podSubnetCidr,
|
|
116
|
+
region: r.region,
|
|
117
|
+
privateIpGoogleAccess: true,
|
|
118
|
+
...flowConfig,
|
|
119
|
+
} as Record<string, unknown>);
|
|
120
|
+
|
|
121
|
+
const routerName = `${name}-${alias}`;
|
|
122
|
+
|
|
123
|
+
routerEntries[`router_${alias}`] = new Router({
|
|
124
|
+
...meta("router", routerName),
|
|
125
|
+
networkRef: { name },
|
|
126
|
+
region: r.region,
|
|
127
|
+
} as Record<string, unknown>);
|
|
128
|
+
|
|
129
|
+
natEntries[`nat_${alias}`] = new RouterNAT({
|
|
130
|
+
...meta("nat", routerName),
|
|
131
|
+
routerRef: { name: routerName },
|
|
132
|
+
region: r.region,
|
|
133
|
+
natIpAllocateOption: "AUTO_ONLY",
|
|
134
|
+
sourceSubnetworkIpRangesToNat: "ALL_SUBNETWORKS_ALL_PRIMARY_IP_RANGES",
|
|
135
|
+
} as Record<string, unknown>);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Allow all TCP/UDP/ICMP between every subnet CIDR in the VPC.
|
|
139
|
+
const firewallInternal = new Firewall({
|
|
140
|
+
...meta("firewall", `${name}-allow-internal`),
|
|
141
|
+
networkRef: { name },
|
|
142
|
+
allow: [
|
|
143
|
+
{ protocol: "tcp", ports: ["0-65535"] },
|
|
144
|
+
{ protocol: "udp", ports: ["0-65535"] },
|
|
145
|
+
{ protocol: "icmp" },
|
|
146
|
+
],
|
|
147
|
+
sourceRanges: allCidrs,
|
|
148
|
+
} as Record<string, unknown>);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
network,
|
|
152
|
+
...subnetEntries,
|
|
153
|
+
...routerEntries,
|
|
154
|
+
...natEntries,
|
|
155
|
+
firewallInternal,
|
|
156
|
+
};
|
|
157
|
+
}, "MultiRegionVpc");
|
package/src/coverage.test.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ export { GCP, ProjectId, Region, Zone } from "./pseudo";
|
|
|
15
15
|
export { GcpAnnotations } from "./variables";
|
|
16
16
|
|
|
17
17
|
// Generated entities — export everything from generated index
|
|
18
|
-
// After running `
|
|
18
|
+
// After running `npm run generate`, this re-exports all Config Connector resource classes
|
|
19
19
|
export * from "./generated/index";
|
|
20
20
|
|
|
21
21
|
// Composites
|
package/src/lsp/hover.test.ts
CHANGED
package/src/package-cli.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
2
|
/**
|
|
3
|
-
* Thin entry point for `
|
|
3
|
+
* Thin entry point for `npm run bundle` in lexicon-gcp.
|
|
4
4
|
* Generates src/generated/ files and writes dist/ bundle.
|
|
5
5
|
*
|
|
6
6
|
* NOTE: Uses top-level await (matching AWS/Azure pattern) to avoid
|
package/src/plugin.test.ts
CHANGED
package/src/plugin.ts
CHANGED
|
@@ -5,9 +5,7 @@
|
|
|
5
5
|
* import parsing, and code generation for GCP Config Connector resources.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { createRequire } from "module";
|
|
9
8
|
import type { LexiconPlugin, InitTemplateSet, ResourceMetadata } from "@intentius/chant/lexicon";
|
|
10
|
-
const require = createRequire(import.meta.url);
|
|
11
9
|
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
12
10
|
import type { TemplateParser } from "@intentius/chant/import/parser";
|
|
13
11
|
import type { TypeScriptGenerator } from "@intentius/chant/import/generator";
|
|
@@ -17,15 +15,19 @@ import { createSkillsLoader, createDiffTool, createCatalogResource } from "@inte
|
|
|
17
15
|
import { join, dirname } from "path";
|
|
18
16
|
import { fileURLToPath } from "url";
|
|
19
17
|
import { gcpSerializer } from "./serializer";
|
|
18
|
+
import { hardcodedProjectRule } from "./lint/rules/hardcoded-project";
|
|
19
|
+
import { hardcodedRegionRule } from "./lint/rules/hardcoded-region";
|
|
20
|
+
import { publicIamRule } from "./lint/rules/public-iam";
|
|
21
|
+
import { GcpParser } from "./import/parser";
|
|
22
|
+
import { GcpGenerator } from "./import/generator";
|
|
23
|
+
import { gcpCompletions } from "./lsp/completions";
|
|
24
|
+
import { gcpHover } from "./lsp/hover";
|
|
20
25
|
|
|
21
26
|
export const gcpPlugin: LexiconPlugin = {
|
|
22
27
|
name: "gcp",
|
|
23
28
|
serializer: gcpSerializer,
|
|
24
29
|
|
|
25
30
|
lintRules(): LintRule[] {
|
|
26
|
-
const { hardcodedProjectRule } = require("./lint/rules/hardcoded-project");
|
|
27
|
-
const { hardcodedRegionRule } = require("./lint/rules/hardcoded-region");
|
|
28
|
-
const { publicIamRule } = require("./lint/rules/public-iam");
|
|
29
31
|
return [hardcodedProjectRule, hardcodedRegionRule, publicIamRule];
|
|
30
32
|
},
|
|
31
33
|
|
|
@@ -190,22 +192,18 @@ export const bucketReader = new IAMPolicyMember({
|
|
|
190
192
|
},
|
|
191
193
|
|
|
192
194
|
templateParser(): TemplateParser {
|
|
193
|
-
const { GcpParser } = require("./import/parser");
|
|
194
195
|
return new GcpParser();
|
|
195
196
|
},
|
|
196
197
|
|
|
197
198
|
templateGenerator(): TypeScriptGenerator {
|
|
198
|
-
const { GcpGenerator } = require("./import/generator");
|
|
199
199
|
return new GcpGenerator();
|
|
200
200
|
},
|
|
201
201
|
|
|
202
202
|
completionProvider(ctx: CompletionContext): CompletionItem[] {
|
|
203
|
-
const { gcpCompletions } = require("./lsp/completions");
|
|
204
203
|
return gcpCompletions(ctx);
|
|
205
204
|
},
|
|
206
205
|
|
|
207
206
|
hoverProvider(ctx: HoverContext): HoverInfo | undefined {
|
|
208
|
-
const { gcpHover } = require("./lsp/hover");
|
|
209
207
|
return gcpHover(ctx);
|
|
210
208
|
},
|
|
211
209
|
|
package/src/pseudo.test.ts
CHANGED
package/src/serializer.test.ts
CHANGED
package/src/spec/fetch.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, test, expect } from "
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { getCachePath, KCC_VERSION } from "./fetch";
|
|
3
3
|
|
|
4
4
|
describe("fetch", () => {
|
|
@@ -6,7 +6,7 @@ describe("fetch", () => {
|
|
|
6
6
|
const path = getCachePath();
|
|
7
7
|
expect(path).toContain(KCC_VERSION);
|
|
8
8
|
expect(path).toContain(".chant");
|
|
9
|
-
expect(path
|
|
9
|
+
expect(path.endsWith(".tar.gz")).toBe(true);
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
test("custom version in cache path", () => {
|
package/src/spec/parse.test.ts
CHANGED
package/src/validate-cli.ts
CHANGED
package/src/validate.test.ts
CHANGED