@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,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VpcNetwork composite — ComputeNetwork + ComputeSubnetwork + ComputeFirewall + ComputeRouterNAT.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface VpcSubnet {
|
|
6
|
+
/** Subnet name suffix. */
|
|
7
|
+
name: string;
|
|
8
|
+
/** IP CIDR range. */
|
|
9
|
+
ipCidrRange: string;
|
|
10
|
+
/** GCP region. */
|
|
11
|
+
region: string;
|
|
12
|
+
/** Enable private Google access (default: true). */
|
|
13
|
+
privateIpGoogleAccess?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface VpcNetworkProps {
|
|
17
|
+
/** Network name. */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Auto-create subnetworks (default: false — custom mode). */
|
|
20
|
+
autoCreateSubnetworks?: boolean;
|
|
21
|
+
/** Subnets to create in custom mode. */
|
|
22
|
+
subnets?: VpcSubnet[];
|
|
23
|
+
/** Enable Cloud NAT for outbound internet access (default: false). */
|
|
24
|
+
enableNat?: boolean;
|
|
25
|
+
/** NAT region (required if enableNat is true). */
|
|
26
|
+
natRegion?: string;
|
|
27
|
+
/** Allow internal traffic firewall rule (default: true). */
|
|
28
|
+
allowInternalTraffic?: boolean;
|
|
29
|
+
/** Allow SSH from IAP (default: false). */
|
|
30
|
+
allowIapSsh?: boolean;
|
|
31
|
+
/** Additional labels. */
|
|
32
|
+
labels?: Record<string, string>;
|
|
33
|
+
/** Namespace for all resources. */
|
|
34
|
+
namespace?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface VpcNetworkResult {
|
|
38
|
+
network: Record<string, unknown>;
|
|
39
|
+
subnets: Record<string, unknown>[];
|
|
40
|
+
firewalls: Record<string, unknown>[];
|
|
41
|
+
router?: Record<string, unknown>;
|
|
42
|
+
routerNat?: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a VpcNetwork composite.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* import { VpcNetwork } from "@intentius/chant-lexicon-gcp";
|
|
51
|
+
*
|
|
52
|
+
* const { network, subnets, firewalls } = VpcNetwork({
|
|
53
|
+
* name: "my-vpc",
|
|
54
|
+
* subnets: [
|
|
55
|
+
* { name: "app", ipCidrRange: "10.0.0.0/24", region: "us-central1" },
|
|
56
|
+
* { name: "data", ipCidrRange: "10.0.1.0/24", region: "us-central1" },
|
|
57
|
+
* ],
|
|
58
|
+
* enableNat: true,
|
|
59
|
+
* natRegion: "us-central1",
|
|
60
|
+
* });
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function VpcNetwork(props: VpcNetworkProps): VpcNetworkResult {
|
|
64
|
+
const {
|
|
65
|
+
name,
|
|
66
|
+
autoCreateSubnetworks = false,
|
|
67
|
+
subnets: subnetDefs = [],
|
|
68
|
+
enableNat = false,
|
|
69
|
+
natRegion,
|
|
70
|
+
allowInternalTraffic = true,
|
|
71
|
+
allowIapSsh = false,
|
|
72
|
+
labels: extraLabels = {},
|
|
73
|
+
namespace,
|
|
74
|
+
} = props;
|
|
75
|
+
|
|
76
|
+
const commonLabels: Record<string, string> = {
|
|
77
|
+
"app.kubernetes.io/name": name,
|
|
78
|
+
"app.kubernetes.io/managed-by": "chant",
|
|
79
|
+
...extraLabels,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const network: Record<string, unknown> = {
|
|
83
|
+
metadata: {
|
|
84
|
+
name,
|
|
85
|
+
...(namespace && { namespace }),
|
|
86
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "network" },
|
|
87
|
+
},
|
|
88
|
+
autoCreateSubnetworks,
|
|
89
|
+
routingMode: "REGIONAL",
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const subnets: Record<string, unknown>[] = subnetDefs.map((sub) => ({
|
|
93
|
+
metadata: {
|
|
94
|
+
name: `${name}-${sub.name}`,
|
|
95
|
+
...(namespace && { namespace }),
|
|
96
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "subnet" },
|
|
97
|
+
},
|
|
98
|
+
networkRef: { name },
|
|
99
|
+
ipCidrRange: sub.ipCidrRange,
|
|
100
|
+
region: sub.region,
|
|
101
|
+
privateIpGoogleAccess: sub.privateIpGoogleAccess ?? true,
|
|
102
|
+
}));
|
|
103
|
+
|
|
104
|
+
const firewalls: Record<string, unknown>[] = [];
|
|
105
|
+
|
|
106
|
+
if (allowInternalTraffic) {
|
|
107
|
+
firewalls.push({
|
|
108
|
+
metadata: {
|
|
109
|
+
name: `${name}-allow-internal`,
|
|
110
|
+
...(namespace && { namespace }),
|
|
111
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "firewall" },
|
|
112
|
+
},
|
|
113
|
+
networkRef: { name },
|
|
114
|
+
allowed: [
|
|
115
|
+
{ protocol: "tcp", ports: ["0-65535"] },
|
|
116
|
+
{ protocol: "udp", ports: ["0-65535"] },
|
|
117
|
+
{ protocol: "icmp" },
|
|
118
|
+
],
|
|
119
|
+
sourceRanges: subnetDefs.map((s) => s.ipCidrRange),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (allowIapSsh) {
|
|
124
|
+
firewalls.push({
|
|
125
|
+
metadata: {
|
|
126
|
+
name: `${name}-allow-iap-ssh`,
|
|
127
|
+
...(namespace && { namespace }),
|
|
128
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "firewall" },
|
|
129
|
+
},
|
|
130
|
+
networkRef: { name },
|
|
131
|
+
allowed: [{ protocol: "tcp", ports: ["22"] }],
|
|
132
|
+
sourceRanges: ["35.235.240.0/20"], // IAP IP range
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const result: VpcNetworkResult = { network, subnets, firewalls };
|
|
137
|
+
|
|
138
|
+
if (enableNat && natRegion) {
|
|
139
|
+
const routerName = `${name}-router`;
|
|
140
|
+
|
|
141
|
+
result.router = {
|
|
142
|
+
metadata: {
|
|
143
|
+
name: routerName,
|
|
144
|
+
...(namespace && { namespace }),
|
|
145
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "router" },
|
|
146
|
+
},
|
|
147
|
+
networkRef: { name },
|
|
148
|
+
region: natRegion,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
result.routerNat = {
|
|
152
|
+
metadata: {
|
|
153
|
+
name: `${name}-nat`,
|
|
154
|
+
...(namespace && { namespace }),
|
|
155
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "nat" },
|
|
156
|
+
},
|
|
157
|
+
routerRef: { name: routerName },
|
|
158
|
+
region: natRegion,
|
|
159
|
+
natIpAllocateOption: "AUTO_ONLY",
|
|
160
|
+
sourceSubnetworkIpRangesToNat: "ALL_SUBNETWORKS_ALL_IP_RANGES",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { join, dirname } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
|
|
6
|
+
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
7
|
+
const generatedDir = join(pkgDir, "src", "generated");
|
|
8
|
+
const hasGenerated = existsSync(join(generatedDir, "lexicon-gcp.json"));
|
|
9
|
+
|
|
10
|
+
describe("coverage", () => {
|
|
11
|
+
test.skipIf(!hasGenerated)("analyzeGcpCoverage function exists", async () => {
|
|
12
|
+
const { analyzeGcpCoverage } = await import("./coverage");
|
|
13
|
+
expect(typeof analyzeGcpCoverage).toBe("function");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("handles missing generated files gracefully", async () => {
|
|
17
|
+
const { analyzeGcpCoverage } = await import("./coverage");
|
|
18
|
+
// If generated files don't exist, this should throw/exit rather than crash
|
|
19
|
+
if (!hasGenerated) {
|
|
20
|
+
try {
|
|
21
|
+
await analyzeGcpCoverage();
|
|
22
|
+
} catch {
|
|
23
|
+
// Expected — no generated files
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
package/src/coverage.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GCP lexicon coverage analysis.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
computeCoverage,
|
|
7
|
+
overallPct,
|
|
8
|
+
formatSummary,
|
|
9
|
+
formatVerbose,
|
|
10
|
+
checkThresholds,
|
|
11
|
+
type CoverageReport,
|
|
12
|
+
} from "@intentius/chant/codegen/coverage";
|
|
13
|
+
import { readFileSync } from "fs";
|
|
14
|
+
import { join, dirname } from "path";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
computeCoverage,
|
|
19
|
+
overallPct,
|
|
20
|
+
formatSummary,
|
|
21
|
+
formatVerbose,
|
|
22
|
+
checkThresholds,
|
|
23
|
+
type ResourceCoverage,
|
|
24
|
+
type CoverageReport,
|
|
25
|
+
type CoverageThresholds,
|
|
26
|
+
type ThresholdResult,
|
|
27
|
+
} from "@intentius/chant/codegen/coverage";
|
|
28
|
+
|
|
29
|
+
export async function analyzeGcpCoverage(opts?: {
|
|
30
|
+
verbose?: boolean;
|
|
31
|
+
minOverall?: number;
|
|
32
|
+
}): Promise<void> {
|
|
33
|
+
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
34
|
+
const lexiconPath = join(pkgDir, "src", "generated", "lexicon-gcp.json");
|
|
35
|
+
const content = readFileSync(lexiconPath, "utf-8");
|
|
36
|
+
const report = computeCoverage(content);
|
|
37
|
+
|
|
38
|
+
if (opts?.verbose) {
|
|
39
|
+
console.log(formatVerbose(report));
|
|
40
|
+
} else {
|
|
41
|
+
console.log(formatSummary(report));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof opts?.minOverall === "number") {
|
|
45
|
+
const result = checkThresholds(report, { minOverallPct: opts.minOverall });
|
|
46
|
+
if (!result.ok) {
|
|
47
|
+
for (const v of result.violations) console.error(` FAIL: ${v}`);
|
|
48
|
+
throw new Error("Coverage below threshold");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
defaultLabels,
|
|
4
|
+
defaultAnnotations,
|
|
5
|
+
isDefaultLabels,
|
|
6
|
+
isDefaultAnnotations,
|
|
7
|
+
DEFAULT_LABELS_MARKER,
|
|
8
|
+
DEFAULT_ANNOTATIONS_MARKER,
|
|
9
|
+
} from "./default-labels";
|
|
10
|
+
import { DECLARABLE_MARKER } from "@intentius/chant/declarable";
|
|
11
|
+
|
|
12
|
+
describe("defaultLabels", () => {
|
|
13
|
+
test("returns object with correct markers", () => {
|
|
14
|
+
const dl = defaultLabels({ env: "prod" });
|
|
15
|
+
expect(dl[DEFAULT_LABELS_MARKER]).toBe(true);
|
|
16
|
+
expect(dl[DECLARABLE_MARKER]).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("has lexicon gcp", () => {
|
|
20
|
+
const dl = defaultLabels({ env: "prod" });
|
|
21
|
+
expect(dl.lexicon).toBe("gcp");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("has correct entityType", () => {
|
|
25
|
+
const dl = defaultLabels({ env: "prod" });
|
|
26
|
+
expect(dl.entityType).toBe("chant:gcp:defaultLabels");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("labels are accessible", () => {
|
|
30
|
+
const dl = defaultLabels({ env: "prod", team: "platform" });
|
|
31
|
+
expect(dl.labels.env).toBe("prod");
|
|
32
|
+
expect(dl.labels.team).toBe("platform");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("empty labels allowed", () => {
|
|
36
|
+
const dl = defaultLabels({});
|
|
37
|
+
expect(dl.labels).toEqual({});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("defaultAnnotations", () => {
|
|
42
|
+
test("returns object with correct markers", () => {
|
|
43
|
+
const da = defaultAnnotations({ "cnrm.cloud.google.com/project-id": "my-project" });
|
|
44
|
+
expect(da[DEFAULT_ANNOTATIONS_MARKER]).toBe(true);
|
|
45
|
+
expect(da[DECLARABLE_MARKER]).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("has lexicon gcp", () => {
|
|
49
|
+
const da = defaultAnnotations({ note: "hello" });
|
|
50
|
+
expect(da.lexicon).toBe("gcp");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("has correct entityType", () => {
|
|
54
|
+
const da = defaultAnnotations({ note: "hello" });
|
|
55
|
+
expect(da.entityType).toBe("chant:gcp:defaultAnnotations");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("annotations are accessible", () => {
|
|
59
|
+
const da = defaultAnnotations({ "cnrm.cloud.google.com/project-id": "my-project" });
|
|
60
|
+
expect(da.annotations["cnrm.cloud.google.com/project-id"]).toBe("my-project");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("empty annotations allowed", () => {
|
|
64
|
+
const da = defaultAnnotations({});
|
|
65
|
+
expect(da.annotations).toEqual({});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("isDefaultLabels", () => {
|
|
70
|
+
test("returns true for defaultLabels result", () => {
|
|
71
|
+
expect(isDefaultLabels(defaultLabels({ env: "prod" }))).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("returns false for null", () => {
|
|
75
|
+
expect(isDefaultLabels(null)).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("returns false for undefined", () => {
|
|
79
|
+
expect(isDefaultLabels(undefined)).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("returns false for regular objects", () => {
|
|
83
|
+
expect(isDefaultLabels({ labels: { env: "prod" } })).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("returns false for defaultAnnotations", () => {
|
|
87
|
+
expect(isDefaultLabels(defaultAnnotations({ note: "hi" }))).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe("isDefaultAnnotations", () => {
|
|
92
|
+
test("returns true for defaultAnnotations result", () => {
|
|
93
|
+
expect(isDefaultAnnotations(defaultAnnotations({ note: "hi" }))).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("returns false for null", () => {
|
|
97
|
+
expect(isDefaultAnnotations(null)).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("returns false for undefined", () => {
|
|
101
|
+
expect(isDefaultAnnotations(undefined)).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("returns false for regular objects", () => {
|
|
105
|
+
expect(isDefaultAnnotations({ annotations: {} })).toBe(false);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("returns false for defaultLabels", () => {
|
|
109
|
+
expect(isDefaultAnnotations(defaultLabels({ env: "prod" }))).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Labels & Annotations — declares project-wide labels/annotations
|
|
3
|
+
* for all GCP Config Connector resources.
|
|
4
|
+
*
|
|
5
|
+
* When a project exports a `defaultLabels(...)` or `defaultAnnotations(...)`
|
|
6
|
+
* declaration, the serializer automatically injects them into every resource's
|
|
7
|
+
* metadata at synthesis time. Explicit labels/annotations on individual
|
|
8
|
+
* resources take precedence.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { DECLARABLE_MARKER, type Declarable } from "@intentius/chant/declarable";
|
|
12
|
+
|
|
13
|
+
export const DEFAULT_LABELS_MARKER = Symbol.for("chant.gcp.defaultLabels");
|
|
14
|
+
export const DEFAULT_ANNOTATIONS_MARKER = Symbol.for("chant.gcp.defaultAnnotations");
|
|
15
|
+
|
|
16
|
+
export interface DefaultLabels extends Declarable {
|
|
17
|
+
readonly [DEFAULT_LABELS_MARKER]: true;
|
|
18
|
+
readonly [DECLARABLE_MARKER]: true;
|
|
19
|
+
readonly lexicon: "gcp";
|
|
20
|
+
readonly entityType: "chant:gcp:defaultLabels";
|
|
21
|
+
readonly labels: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DefaultAnnotations extends Declarable {
|
|
25
|
+
readonly [DEFAULT_ANNOTATIONS_MARKER]: true;
|
|
26
|
+
readonly [DECLARABLE_MARKER]: true;
|
|
27
|
+
readonly lexicon: "gcp";
|
|
28
|
+
readonly entityType: "chant:gcp:defaultAnnotations";
|
|
29
|
+
readonly annotations: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isDefaultLabels(value: unknown): value is DefaultLabels {
|
|
33
|
+
return (
|
|
34
|
+
typeof value === "object" &&
|
|
35
|
+
value !== null &&
|
|
36
|
+
DEFAULT_LABELS_MARKER in value &&
|
|
37
|
+
(value as Record<symbol, unknown>)[DEFAULT_LABELS_MARKER] === true
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isDefaultAnnotations(value: unknown): value is DefaultAnnotations {
|
|
42
|
+
return (
|
|
43
|
+
typeof value === "object" &&
|
|
44
|
+
value !== null &&
|
|
45
|
+
DEFAULT_ANNOTATIONS_MARKER in value &&
|
|
46
|
+
(value as Record<symbol, unknown>)[DEFAULT_ANNOTATIONS_MARKER] === true
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Declare project-wide default labels for all GCP Config Connector resources.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* import { defaultLabels } from "@intentius/chant-lexicon-gcp";
|
|
56
|
+
*
|
|
57
|
+
* export const labels = defaultLabels({
|
|
58
|
+
* "app.kubernetes.io/managed-by": "chant",
|
|
59
|
+
* "env": "production",
|
|
60
|
+
* });
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function defaultLabels(labels: Record<string, unknown>): DefaultLabels {
|
|
64
|
+
return {
|
|
65
|
+
[DEFAULT_LABELS_MARKER]: true,
|
|
66
|
+
[DECLARABLE_MARKER]: true,
|
|
67
|
+
lexicon: "gcp",
|
|
68
|
+
entityType: "chant:gcp:defaultLabels",
|
|
69
|
+
labels,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Declare project-wide default annotations for all GCP Config Connector resources.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* import { defaultAnnotations, GCP } from "@intentius/chant-lexicon-gcp";
|
|
79
|
+
*
|
|
80
|
+
* export const annotations = defaultAnnotations({
|
|
81
|
+
* "cnrm.cloud.google.com/project-id": GCP.ProjectId,
|
|
82
|
+
* });
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function defaultAnnotations(annotations: Record<string, unknown>): DefaultAnnotations {
|
|
86
|
+
return {
|
|
87
|
+
[DEFAULT_ANNOTATIONS_MARKER]: true,
|
|
88
|
+
[DECLARABLE_MARKER]: true,
|
|
89
|
+
lexicon: "gcp",
|
|
90
|
+
entityType: "chant:gcp:defaultAnnotations",
|
|
91
|
+
annotations,
|
|
92
|
+
};
|
|
93
|
+
}
|