@intentius/chant-lexicon-gcp 0.0.22 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,15 @@
2
2
  * SecureProject composite — Project + IAMAuditConfig + Service enablement + owner IAM + LoggingSink.
3
3
  */
4
4
 
5
+ import { Composite, mergeDefaults } from "@intentius/chant";
6
+ import {
7
+ ResourcemanagerProject,
8
+ IAMAuditConfig,
9
+ ServiceusageService,
10
+ IAMPolicyMember,
11
+ LogSink,
12
+ } from "../generated";
13
+
5
14
  export interface SecureProjectProps {
6
15
  /** Project name (also used as project ID). */
7
16
  name: string;
@@ -23,17 +32,16 @@ export interface SecureProjectProps {
23
32
  labels?: Record<string, string>;
24
33
  /** Namespace for all resources. */
25
34
  namespace?: string;
35
+ /** Per-member defaults for customizing individual resources. */
36
+ defaults?: {
37
+ project?: Partial<ConstructorParameters<typeof ResourcemanagerProject>[0]>;
38
+ auditConfig?: Partial<ConstructorParameters<typeof IAMAuditConfig>[0]>;
39
+ ownerIam?: Partial<ConstructorParameters<typeof IAMPolicyMember>[0]>;
40
+ loggingSink?: Partial<ConstructorParameters<typeof LogSink>[0]>;
41
+ };
26
42
  }
27
43
 
28
- export interface SecureProjectResult {
29
- project: Record<string, unknown>;
30
- auditConfig: Record<string, unknown>;
31
- services: Record<string, unknown>[];
32
- ownerIam?: Record<string, unknown>;
33
- loggingSink?: Record<string, unknown>;
34
- }
35
-
36
- export function SecureProject(props: SecureProjectProps): SecureProjectResult {
44
+ export const SecureProject = Composite<SecureProjectProps>((props) => {
37
45
  const {
38
46
  name,
39
47
  orgId,
@@ -51,6 +59,7 @@ export function SecureProject(props: SecureProjectProps): SecureProjectResult {
51
59
  loggingSinkFilter = 'logName:"cloudaudit.googleapis.com"',
52
60
  labels: extraLabels = {},
53
61
  namespace,
62
+ defaults: defs,
54
63
  } = props;
55
64
 
56
65
  const commonLabels: Record<string, string> = {
@@ -59,7 +68,7 @@ export function SecureProject(props: SecureProjectProps): SecureProjectResult {
59
68
  ...extraLabels,
60
69
  };
61
70
 
62
- const project: Record<string, unknown> = {
71
+ const project = new ResourcemanagerProject(mergeDefaults({
63
72
  metadata: {
64
73
  name,
65
74
  ...(namespace && { namespace }),
@@ -68,9 +77,9 @@ export function SecureProject(props: SecureProjectProps): SecureProjectResult {
68
77
  ...(orgId && { organizationRef: { external: orgId } }),
69
78
  ...(folderId && { folderRef: { external: folderId } }),
70
79
  ...(billingAccountRef && { billingAccountRef: { external: billingAccountRef } }),
71
- };
80
+ } as Record<string, unknown>, defs?.project));
72
81
 
73
- const auditConfig: Record<string, unknown> = {
82
+ const auditConfig = new IAMAuditConfig(mergeDefaults({
74
83
  metadata: {
75
84
  name: `${name}-audit`,
76
85
  ...(namespace && { namespace }),
@@ -82,21 +91,27 @@ export function SecureProject(props: SecureProjectProps): SecureProjectResult {
82
91
  { logType: "DATA_READ" },
83
92
  { logType: "DATA_WRITE" },
84
93
  ],
85
- };
94
+ } as Record<string, unknown>, defs?.auditConfig));
86
95
 
87
- const services = enabledApis.map((api) => ({
88
- metadata: {
89
- name: `${name}-${api.split(".")[0]}`,
90
- ...(namespace && { namespace }),
91
- labels: { ...commonLabels, "app.kubernetes.io/component": "service" },
92
- },
93
- resourceID: api,
94
- }));
96
+ // Spread services into named members (service_compute, service_container, etc.)
97
+ // so the Composite validator accepts them as individual Declarables.
98
+ const serviceEntries: Record<string, any> = {};
99
+ for (const api of enabledApis) {
100
+ const key = `service_${api.split(".")[0]}`;
101
+ serviceEntries[key] = new ServiceusageService({
102
+ metadata: {
103
+ name: `${name}-${api.split(".")[0]}`,
104
+ ...(namespace && { namespace }),
105
+ labels: { ...commonLabels, "app.kubernetes.io/component": "service" },
106
+ },
107
+ resourceID: api,
108
+ } as Record<string, unknown>);
109
+ }
95
110
 
96
- const result: SecureProjectResult = { project, auditConfig, services };
111
+ const result: Record<string, any> = { project, auditConfig, ...serviceEntries };
97
112
 
98
113
  if (owner) {
99
- result.ownerIam = {
114
+ result.ownerIam = new IAMPolicyMember(mergeDefaults({
100
115
  metadata: {
101
116
  name: `${name}-owner`,
102
117
  ...(namespace && { namespace }),
@@ -109,11 +124,11 @@ export function SecureProject(props: SecureProjectProps): SecureProjectResult {
109
124
  kind: "Project",
110
125
  name,
111
126
  },
112
- };
127
+ } as Record<string, unknown>, defs?.ownerIam));
113
128
  }
114
129
 
115
130
  if (loggingSinkDestination) {
116
- result.loggingSink = {
131
+ result.loggingSink = new LogSink(mergeDefaults({
117
132
  metadata: {
118
133
  name: `${name}-audit-sink`,
119
134
  ...(namespace && { namespace }),
@@ -121,8 +136,8 @@ export function SecureProject(props: SecureProjectProps): SecureProjectResult {
121
136
  },
122
137
  destination: loggingSinkDestination,
123
138
  filter: loggingSinkFilter,
124
- };
139
+ } as Record<string, unknown>, defs?.loggingSink));
125
140
  }
126
141
 
127
142
  return result;
128
- }
143
+ }, "SecureProject");
@@ -2,6 +2,14 @@
2
2
  * VpcNetwork composite — ComputeNetwork + ComputeSubnetwork + ComputeFirewall + ComputeRouterNAT.
3
3
  */
4
4
 
5
+ import { Composite, mergeDefaults } from "@intentius/chant";
6
+ import { VPCNetwork, Subnetwork, Firewall, Router, RouterNAT } from "../generated";
7
+
8
+ export interface VpcSubnetSecondaryRange {
9
+ rangeName: string;
10
+ ipCidrRange: string;
11
+ }
12
+
5
13
  export interface VpcSubnet {
6
14
  /** Subnet name suffix. */
7
15
  name: string;
@@ -11,6 +19,8 @@ export interface VpcSubnet {
11
19
  region: string;
12
20
  /** Enable private Google access (default: true). */
13
21
  privateIpGoogleAccess?: boolean;
22
+ /** Secondary IP ranges for VPC-native GKE pods/services. */
23
+ secondaryIpRanges?: VpcSubnetSecondaryRange[];
14
24
  }
15
25
 
16
26
  export interface VpcNetworkProps {
@@ -32,14 +42,12 @@ export interface VpcNetworkProps {
32
42
  labels?: Record<string, string>;
33
43
  /** Namespace for all resources. */
34
44
  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>;
45
+ /** Per-member defaults for customizing individual resources. */
46
+ defaults?: {
47
+ network?: Partial<ConstructorParameters<typeof VPCNetwork>[0]>;
48
+ router?: Partial<ConstructorParameters<typeof Router>[0]>;
49
+ routerNat?: Partial<ConstructorParameters<typeof RouterNAT>[0]>;
50
+ };
43
51
  }
44
52
 
45
53
  /**
@@ -60,7 +68,7 @@ export interface VpcNetworkResult {
60
68
  * });
61
69
  * ```
62
70
  */
63
- export function VpcNetwork(props: VpcNetworkProps): VpcNetworkResult {
71
+ export const VpcNetwork = Composite<VpcNetworkProps>((props) => {
64
72
  const {
65
73
  name,
66
74
  autoCreateSubnetworks = false,
@@ -71,6 +79,7 @@ export function VpcNetwork(props: VpcNetworkProps): VpcNetworkResult {
71
79
  allowIapSsh = false,
72
80
  labels: extraLabels = {},
73
81
  namespace,
82
+ defaults: defs,
74
83
  } = props;
75
84
 
76
85
  const commonLabels: Record<string, string> = {
@@ -79,7 +88,7 @@ export function VpcNetwork(props: VpcNetworkProps): VpcNetworkResult {
79
88
  ...extraLabels,
80
89
  };
81
90
 
82
- const network: Record<string, unknown> = {
91
+ const network = new VPCNetwork(mergeDefaults({
83
92
  metadata: {
84
93
  name,
85
94
  ...(namespace && { namespace }),
@@ -87,58 +96,66 @@ export function VpcNetwork(props: VpcNetworkProps): VpcNetworkResult {
87
96
  },
88
97
  autoCreateSubnetworks,
89
98
  routingMode: "REGIONAL",
90
- };
99
+ } as Record<string, unknown>, defs?.network));
91
100
 
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
- }));
101
+ // Spread subnets into named members (subnet_<name>) for Composite validation.
102
+ const subnetEntries: Record<string, any> = {};
103
+ for (const sub of subnetDefs) {
104
+ subnetEntries[`subnet_${sub.name}`] = new Subnetwork({
105
+ metadata: {
106
+ name: `${name}-${sub.name}`,
107
+ ...(namespace && { namespace }),
108
+ labels: { ...commonLabels, "app.kubernetes.io/component": "subnet" },
109
+ },
110
+ networkRef: { name },
111
+ ipCidrRange: sub.ipCidrRange,
112
+ region: sub.region,
113
+ privateIpGoogleAccess: sub.privateIpGoogleAccess ?? true,
114
+ ...(sub.secondaryIpRanges && sub.secondaryIpRanges.length > 0 && {
115
+ secondaryIpRange: sub.secondaryIpRanges,
116
+ }),
117
+ } as Record<string, unknown>);
118
+ }
103
119
 
104
- const firewalls: Record<string, unknown>[] = [];
120
+ // Spread firewalls into named members for Composite validation.
121
+ const firewallEntries: Record<string, any> = {};
105
122
 
106
123
  if (allowInternalTraffic) {
107
- firewalls.push({
124
+ firewallEntries.firewallAllowInternal = new Firewall({
108
125
  metadata: {
109
126
  name: `${name}-allow-internal`,
110
127
  ...(namespace && { namespace }),
111
128
  labels: { ...commonLabels, "app.kubernetes.io/component": "firewall" },
112
129
  },
113
130
  networkRef: { name },
114
- allowed: [
131
+ allow: [
115
132
  { protocol: "tcp", ports: ["0-65535"] },
116
133
  { protocol: "udp", ports: ["0-65535"] },
117
134
  { protocol: "icmp" },
118
135
  ],
119
136
  sourceRanges: subnetDefs.map((s) => s.ipCidrRange),
120
- });
137
+ } as Record<string, unknown>);
121
138
  }
122
139
 
123
140
  if (allowIapSsh) {
124
- firewalls.push({
141
+ firewallEntries.firewallAllowIapSsh = new Firewall({
125
142
  metadata: {
126
143
  name: `${name}-allow-iap-ssh`,
127
144
  ...(namespace && { namespace }),
128
145
  labels: { ...commonLabels, "app.kubernetes.io/component": "firewall" },
129
146
  },
130
147
  networkRef: { name },
131
- allowed: [{ protocol: "tcp", ports: ["22"] }],
148
+ allow: [{ protocol: "tcp", ports: ["22"] }],
132
149
  sourceRanges: ["35.235.240.0/20"], // IAP IP range
133
- });
150
+ } as Record<string, unknown>);
134
151
  }
135
152
 
136
- const result: VpcNetworkResult = { network, subnets, firewalls };
153
+ const result: Record<string, any> = { network, ...subnetEntries, ...firewallEntries };
137
154
 
138
155
  if (enableNat && natRegion) {
139
156
  const routerName = `${name}-router`;
140
157
 
141
- result.router = {
158
+ result.router = new Router(mergeDefaults({
142
159
  metadata: {
143
160
  name: routerName,
144
161
  ...(namespace && { namespace }),
@@ -146,9 +163,9 @@ export function VpcNetwork(props: VpcNetworkProps): VpcNetworkResult {
146
163
  },
147
164
  networkRef: { name },
148
165
  region: natRegion,
149
- };
166
+ } as Record<string, unknown>, defs?.router));
150
167
 
151
- result.routerNat = {
168
+ result.routerNat = new RouterNAT(mergeDefaults({
152
169
  metadata: {
153
170
  name: `${name}-nat`,
154
171
  ...(namespace && { namespace }),
@@ -158,8 +175,8 @@ export function VpcNetwork(props: VpcNetworkProps): VpcNetworkResult {
158
175
  region: natRegion,
159
176
  natIpAllocateOption: "AUTO_ONLY",
160
177
  sourceSubnetworkIpRangesToNat: "ALL_SUBNETWORKS_ALL_IP_RANGES",
161
- };
178
+ } as Record<string, unknown>, defs?.routerNat));
162
179
  }
163
180
 
164
181
  return result;
165
- }
182
+ }, "VpcNetwork");
package/src/index.ts CHANGED
@@ -20,20 +20,22 @@ export * from "./generated/index";
20
20
 
21
21
  // Composites
22
22
  export {
23
- GkeCluster, CloudRunService, CloudSqlInstance, GcsBucket, VpcNetwork,
23
+ GkeCluster, CloudRunServiceComposite, CloudSqlInstance, GcsBucket, VpcNetwork,
24
24
  PubSubPipeline, CloudFunctionWithTrigger, PrivateService, ManagedCertificate, SecureProject,
25
+ MemorystoreRedis,
25
26
  } from "./composites/index";
26
27
  export type {
27
- GkeClusterProps, GkeClusterResult,
28
- CloudRunServiceProps, CloudRunServiceResult,
29
- CloudSqlInstanceProps, CloudSqlInstanceResult,
30
- GcsBucketProps, GcsBucketResult,
31
- VpcNetworkProps, VpcNetworkResult, VpcSubnet,
32
- PubSubPipelineProps, PubSubPipelineResult,
33
- CloudFunctionWithTriggerProps, CloudFunctionWithTriggerResult,
34
- PrivateServiceProps, PrivateServiceResult,
35
- ManagedCertificateProps, ManagedCertificateResult,
36
- SecureProjectProps, SecureProjectResult,
28
+ GkeClusterProps,
29
+ CloudRunServiceProps,
30
+ CloudSqlInstanceProps,
31
+ GcsBucketProps,
32
+ VpcNetworkProps, VpcSubnet,
33
+ PubSubPipelineProps,
34
+ CloudFunctionWithTriggerProps,
35
+ PrivateServiceProps,
36
+ ManagedCertificateProps,
37
+ SecureProjectProps,
38
+ MemorystoreRedisProps,
37
39
  } from "./composites/index";
38
40
 
39
41
  // IAM role constants
@@ -54,7 +54,8 @@ metadata:
54
54
  spec:
55
55
  location: US
56
56
  encryption:
57
- defaultKmsKeyName: projects/p/locations/l/keyRings/kr/cryptoKeys/k
57
+ kmsKeyRef:
58
+ external: projects/p/locations/l/keyRings/kr/cryptoKeys/k
58
59
  `;
59
60
  const diags = wgc101.check(makeCtx(yaml));
60
61
  const bucketDiags = diags.filter(d => d.entity === "my-bucket");
@@ -30,7 +30,8 @@ metadata:
30
30
  spec:
31
31
  location: US
32
32
  encryption:
33
- defaultKmsKeyName: projects/p/locations/l/keyRings/kr/cryptoKeys/k
33
+ kmsKeyRef:
34
+ external: projects/p/locations/l/keyRings/kr/cryptoKeys/k
34
35
  `;
35
36
  const diags = wgc101.check(makeCtx(yaml));
36
37
  const bucketDiags = diags.filter(d => d.entity === "my-bucket");
@@ -30,7 +30,7 @@ export const wgc101: PostSynthCheck = {
30
30
  diagnostics.push({
31
31
  checkId: "WGC101",
32
32
  severity: "warning",
33
- message: `StorageBucket "${name}" has no encryption configuration — consider adding spec.encryption.defaultKmsKeyName`,
33
+ message: `StorageBucket "${name}" has no encryption configuration — consider adding spec.encryption.kmsKeyRef`,
34
34
  entity: name,
35
35
  lexicon: "gcp",
36
36
  });