@intentius/chant-lexicon-gcp 0.0.22 → 0.0.24
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 +7 -7
- package/dist/manifest.json +1 -1
- package/dist/rules/wgc101.ts +1 -1
- package/dist/skills/{chant-gke.md → chant-gcp-gke.md} +1 -1
- package/dist/skills/chant-gcp-patterns.md +3 -2
- package/dist/skills/chant-gcp-security.md +3 -2
- package/dist/skills/chant-gcp.md +360 -28
- package/package.json +1 -1
- package/src/composites/cloud-function.ts +23 -15
- package/src/composites/cloud-run-service.ts +20 -13
- package/src/composites/cloud-sql-instance.ts +18 -14
- package/src/composites/composites.test.ts +94 -62
- package/src/composites/gcs-bucket.ts +13 -9
- package/src/composites/gke-cluster.ts +91 -16
- package/src/composites/index.ts +11 -11
- package/src/composites/managed-certificate.ts +19 -15
- package/src/composites/private-service.ts +23 -15
- package/src/composites/pubsub-pipeline.ts +30 -18
- package/src/composites/secure-project.ts +42 -27
- package/src/composites/vpc-network.ts +42 -35
- package/src/index.ts +11 -11
- package/src/lint/post-synth/post-synth.test.ts +2 -1
- package/src/lint/post-synth/wgc101.test.ts +2 -1
- package/src/lint/post-synth/wgc101.ts +1 -1
- package/src/plugin.ts +92 -180
- package/src/skills/{chant-gke.md → chant-gcp-gke.md} +1 -1
- package/src/skills/chant-gcp-patterns.md +3 -2
- package/src/skills/chant-gcp-security.md +3 -2
- package/src/skills/chant-gcp.md +360 -28
|
@@ -5,9 +5,14 @@
|
|
|
5
5
|
* workload identity and autoscaling.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
9
|
+
import { GKECluster, NodePool } from "../generated";
|
|
10
|
+
|
|
8
11
|
export interface GkeClusterProps {
|
|
9
12
|
/** Cluster name. */
|
|
10
13
|
name: string;
|
|
14
|
+
/** GCP project ID for workload identity pool. Falls back to GCP_PROJECT_ID env var. */
|
|
15
|
+
projectId?: string;
|
|
11
16
|
/** GCP region for regional cluster (default: uses GCP.Region). */
|
|
12
17
|
location?: string;
|
|
13
18
|
/** Initial node count per zone (default: 1). */
|
|
@@ -22,21 +27,46 @@ export interface GkeClusterProps {
|
|
|
22
27
|
workloadIdentity?: boolean;
|
|
23
28
|
/** Disk size in GB for nodes (default: 100). */
|
|
24
29
|
diskSizeGb?: number;
|
|
30
|
+
/** Boot disk type for nodes (default: "pd-standard"). */
|
|
31
|
+
diskType?: string;
|
|
32
|
+
/** VPC network name (if omitted, uses default network). */
|
|
33
|
+
network?: string;
|
|
34
|
+
/** Subnetwork name for the cluster nodes. */
|
|
35
|
+
subnetwork?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Enable private nodes — nodes have only internal IPs, no external IPs.
|
|
38
|
+
* Requires Cloud NAT for egress. Keeps master endpoint public so kubectl works from outside.
|
|
39
|
+
* Requires masterCidr when enabled.
|
|
40
|
+
*/
|
|
41
|
+
privateNodes?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* CIDR block for the master's private endpoint (e.g. "172.16.0.0/28").
|
|
44
|
+
* Must be /28, unique per cluster, and not overlap with node/pod CIDRs.
|
|
45
|
+
* Required when privateNodes is true.
|
|
46
|
+
*/
|
|
47
|
+
masterCidr?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Restrict node pools to specific zones within the region.
|
|
50
|
+
* Useful when a region has capacity issues in some zones.
|
|
51
|
+
* E.g. ["us-east4-b"] to use only zone b of us-east4.
|
|
52
|
+
*/
|
|
53
|
+
nodeLocations?: string[];
|
|
25
54
|
/** GKE release channel (default: "REGULAR"). */
|
|
26
55
|
releaseChannel?: "RAPID" | "REGULAR" | "STABLE";
|
|
27
56
|
/** Additional labels. */
|
|
28
57
|
labels?: Record<string, string>;
|
|
29
58
|
/** Namespace for all resources. */
|
|
30
59
|
namespace?: string;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
60
|
+
/** Per-member defaults for customizing individual resources. */
|
|
61
|
+
defaults?: {
|
|
62
|
+
cluster?: Partial<ConstructorParameters<typeof GKECluster>[0]>;
|
|
63
|
+
nodePool?: Partial<ConstructorParameters<typeof NodePool>[0]>;
|
|
64
|
+
defaultPool?: Partial<ConstructorParameters<typeof NodePool>[0]>;
|
|
65
|
+
};
|
|
36
66
|
}
|
|
37
67
|
|
|
38
68
|
/**
|
|
39
|
-
* Create a GkeCluster composite — returns
|
|
69
|
+
* Create a GkeCluster composite — returns declarable instances for
|
|
40
70
|
* a ContainerCluster and ContainerNodePool.
|
|
41
71
|
*
|
|
42
72
|
* @example
|
|
@@ -50,9 +80,10 @@ export interface GkeClusterResult {
|
|
|
50
80
|
* });
|
|
51
81
|
* ```
|
|
52
82
|
*/
|
|
53
|
-
export
|
|
83
|
+
export const GkeCluster = Composite<GkeClusterProps>((props) => {
|
|
54
84
|
const {
|
|
55
85
|
name,
|
|
86
|
+
projectId: rawProjectId,
|
|
56
87
|
location,
|
|
57
88
|
initialNodeCount = 1,
|
|
58
89
|
machineType = "e2-medium",
|
|
@@ -60,11 +91,20 @@ export function GkeCluster(props: GkeClusterProps): GkeClusterResult {
|
|
|
60
91
|
maxNodeCount = 5,
|
|
61
92
|
workloadIdentity = true,
|
|
62
93
|
diskSizeGb = 100,
|
|
94
|
+
diskType = "pd-standard",
|
|
95
|
+
network: networkName,
|
|
96
|
+
subnetwork: subnetworkName,
|
|
97
|
+
privateNodes = false,
|
|
98
|
+
masterCidr,
|
|
99
|
+
nodeLocations,
|
|
63
100
|
releaseChannel = "REGULAR",
|
|
64
101
|
labels: extraLabels = {},
|
|
65
102
|
namespace,
|
|
103
|
+
defaults: defs,
|
|
66
104
|
} = props;
|
|
67
105
|
|
|
106
|
+
const projectId = rawProjectId ?? process.env.GCP_PROJECT_ID ?? "PROJECT_ID";
|
|
107
|
+
|
|
68
108
|
const commonLabels: Record<string, string> = {
|
|
69
109
|
"app.kubernetes.io/name": name,
|
|
70
110
|
"app.kubernetes.io/managed-by": "chant",
|
|
@@ -72,29 +112,47 @@ export function GkeCluster(props: GkeClusterProps): GkeClusterResult {
|
|
|
72
112
|
};
|
|
73
113
|
|
|
74
114
|
const clusterSpec: Record<string, unknown> = {
|
|
75
|
-
|
|
76
|
-
|
|
115
|
+
// GKE API requires initialNodeCount > 0 even when using a separate
|
|
116
|
+
// ContainerNodePool. Force pd-standard so the default pool doesn't
|
|
117
|
+
// consume SSD_TOTAL_GB quota (pd-balanced counts as SSD).
|
|
118
|
+
initialNodeCount: 1,
|
|
119
|
+
nodeConfig: {
|
|
120
|
+
machineType,
|
|
121
|
+
diskType: "pd-standard",
|
|
122
|
+
diskSizeGb: 50,
|
|
123
|
+
},
|
|
77
124
|
releaseChannel: { channel: releaseChannel },
|
|
78
125
|
...(location && { location }),
|
|
126
|
+
...(networkName && { networkRef: { name: networkName } }),
|
|
127
|
+
...(subnetworkName && { subnetworkRef: { name: subnetworkName } }),
|
|
128
|
+
...(nodeLocations && { nodeLocations }),
|
|
129
|
+
...(privateNodes && {
|
|
130
|
+
privateClusterConfig: {
|
|
131
|
+
enablePrivateNodes: true,
|
|
132
|
+
// Keep master endpoint public so kubectl works from outside the VPC.
|
|
133
|
+
enablePrivateEndpoint: false,
|
|
134
|
+
...(masterCidr && { masterIpv4CidrBlock: masterCidr }),
|
|
135
|
+
},
|
|
136
|
+
}),
|
|
79
137
|
};
|
|
80
138
|
|
|
81
139
|
if (workloadIdentity) {
|
|
82
140
|
clusterSpec.workloadIdentityConfig = {
|
|
83
|
-
workloadPool:
|
|
141
|
+
workloadPool: `${projectId}.svc.id.goog`,
|
|
84
142
|
};
|
|
85
143
|
}
|
|
86
144
|
|
|
87
|
-
const cluster
|
|
145
|
+
const cluster = new GKECluster(mergeDefaults({
|
|
88
146
|
metadata: {
|
|
89
147
|
name,
|
|
90
148
|
...(namespace && { namespace }),
|
|
91
149
|
labels: { ...commonLabels, "app.kubernetes.io/component": "cluster" },
|
|
92
150
|
},
|
|
93
151
|
...clusterSpec,
|
|
94
|
-
};
|
|
152
|
+
} as Record<string, unknown>, defs?.cluster));
|
|
95
153
|
|
|
96
154
|
const nodePoolName = `${name}-nodes`;
|
|
97
|
-
const nodePool
|
|
155
|
+
const nodePool = new NodePool(mergeDefaults({
|
|
98
156
|
metadata: {
|
|
99
157
|
name: nodePoolName,
|
|
100
158
|
...(namespace && { namespace }),
|
|
@@ -109,6 +167,7 @@ export function GkeCluster(props: GkeClusterProps): GkeClusterResult {
|
|
|
109
167
|
nodeConfig: {
|
|
110
168
|
machineType,
|
|
111
169
|
diskSizeGb,
|
|
170
|
+
diskType,
|
|
112
171
|
oauthScopes: ["https://www.googleapis.com/auth/cloud-platform"],
|
|
113
172
|
...(workloadIdentity && {
|
|
114
173
|
workloadMetadataConfig: { mode: "GKE_METADATA" },
|
|
@@ -119,7 +178,23 @@ export function GkeCluster(props: GkeClusterProps): GkeClusterResult {
|
|
|
119
178
|
autoUpgrade: true,
|
|
120
179
|
},
|
|
121
180
|
...(location && { location }),
|
|
122
|
-
};
|
|
181
|
+
} as Record<string, unknown>, defs?.nodePool));
|
|
123
182
|
|
|
124
|
-
|
|
125
|
-
|
|
183
|
+
// Adopt and scale the GKE-required default pool to 0.
|
|
184
|
+
// resourceID maps this K8s resource to the GCP pool named "default-pool".
|
|
185
|
+
// Config Connector retries until the workload pool has nodes, at which
|
|
186
|
+
// point GKE accepts the resize-to-zero.
|
|
187
|
+
const defaultPool = new NodePool(mergeDefaults({
|
|
188
|
+
metadata: {
|
|
189
|
+
name: `${name}-default-pool`,
|
|
190
|
+
...(namespace && { namespace }),
|
|
191
|
+
labels: { ...commonLabels, "app.kubernetes.io/component": "default-pool" },
|
|
192
|
+
},
|
|
193
|
+
resourceID: "default-pool",
|
|
194
|
+
clusterRef: { name },
|
|
195
|
+
nodeCount: 0,
|
|
196
|
+
...(location && { location }),
|
|
197
|
+
} as Record<string, unknown>, defs?.defaultPool));
|
|
198
|
+
|
|
199
|
+
return { cluster, nodePool, defaultPool };
|
|
200
|
+
}, "GkeCluster");
|
package/src/composites/index.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
export { GkeCluster } from "./gke-cluster";
|
|
2
|
-
export type { GkeClusterProps
|
|
3
|
-
export {
|
|
4
|
-
export type { CloudRunServiceProps
|
|
2
|
+
export type { GkeClusterProps } from "./gke-cluster";
|
|
3
|
+
export { CloudRunServiceComposite } from "./cloud-run-service";
|
|
4
|
+
export type { CloudRunServiceProps } from "./cloud-run-service";
|
|
5
5
|
export { CloudSqlInstance } from "./cloud-sql-instance";
|
|
6
|
-
export type { CloudSqlInstanceProps
|
|
6
|
+
export type { CloudSqlInstanceProps } from "./cloud-sql-instance";
|
|
7
7
|
export { GcsBucket } from "./gcs-bucket";
|
|
8
|
-
export type { GcsBucketProps
|
|
8
|
+
export type { GcsBucketProps } from "./gcs-bucket";
|
|
9
9
|
export { VpcNetwork } from "./vpc-network";
|
|
10
|
-
export type { VpcNetworkProps,
|
|
10
|
+
export type { VpcNetworkProps, VpcSubnet } from "./vpc-network";
|
|
11
11
|
export { PubSubPipeline } from "./pubsub-pipeline";
|
|
12
|
-
export type { PubSubPipelineProps
|
|
12
|
+
export type { PubSubPipelineProps } from "./pubsub-pipeline";
|
|
13
13
|
export { CloudFunctionWithTrigger } from "./cloud-function";
|
|
14
|
-
export type { CloudFunctionWithTriggerProps
|
|
14
|
+
export type { CloudFunctionWithTriggerProps } from "./cloud-function";
|
|
15
15
|
export { PrivateService } from "./private-service";
|
|
16
|
-
export type { PrivateServiceProps
|
|
16
|
+
export type { PrivateServiceProps } from "./private-service";
|
|
17
17
|
export { ManagedCertificate } from "./managed-certificate";
|
|
18
|
-
export type { ManagedCertificateProps
|
|
18
|
+
export type { ManagedCertificateProps } from "./managed-certificate";
|
|
19
19
|
export { SecureProject } from "./secure-project";
|
|
20
|
-
export type { SecureProjectProps
|
|
20
|
+
export type { SecureProjectProps } from "./secure-project";
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* ManagedCertificate composite — ManagedSslCertificate + optional TargetHttpsProxy + UrlMap.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
6
|
+
import { ManagedSSLCertificate, TargetHTTPSProxy, URLMap } from "../generated";
|
|
7
|
+
|
|
5
8
|
export interface ManagedCertificateProps {
|
|
6
9
|
/** Certificate name. */
|
|
7
10
|
name: string;
|
|
@@ -15,15 +18,15 @@ export interface ManagedCertificateProps {
|
|
|
15
18
|
labels?: Record<string, string>;
|
|
16
19
|
/** Namespace for all resources. */
|
|
17
20
|
namespace?: string;
|
|
21
|
+
/** Per-member defaults for customizing individual resources. */
|
|
22
|
+
defaults?: {
|
|
23
|
+
certificate?: Partial<ConstructorParameters<typeof ManagedSSLCertificate>[0]>;
|
|
24
|
+
targetHttpsProxy?: Partial<ConstructorParameters<typeof TargetHTTPSProxy>[0]>;
|
|
25
|
+
urlMap?: Partial<ConstructorParameters<typeof URLMap>[0]>;
|
|
26
|
+
};
|
|
18
27
|
}
|
|
19
28
|
|
|
20
|
-
export
|
|
21
|
-
certificate: Record<string, unknown>;
|
|
22
|
-
targetHttpsProxy?: Record<string, unknown>;
|
|
23
|
-
urlMap?: Record<string, unknown>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function ManagedCertificate(props: ManagedCertificateProps): ManagedCertificateResult {
|
|
29
|
+
export const ManagedCertificate = Composite<ManagedCertificateProps>((props) => {
|
|
27
30
|
const {
|
|
28
31
|
name,
|
|
29
32
|
domains,
|
|
@@ -31,6 +34,7 @@ export function ManagedCertificate(props: ManagedCertificateProps): ManagedCerti
|
|
|
31
34
|
backendServiceName,
|
|
32
35
|
labels: extraLabels = {},
|
|
33
36
|
namespace,
|
|
37
|
+
defaults: defs,
|
|
34
38
|
} = props;
|
|
35
39
|
|
|
36
40
|
const commonLabels: Record<string, string> = {
|
|
@@ -39,7 +43,7 @@ export function ManagedCertificate(props: ManagedCertificateProps): ManagedCerti
|
|
|
39
43
|
...extraLabels,
|
|
40
44
|
};
|
|
41
45
|
|
|
42
|
-
const certificate
|
|
46
|
+
const certificate = new ManagedSSLCertificate(mergeDefaults({
|
|
43
47
|
metadata: {
|
|
44
48
|
name,
|
|
45
49
|
...(namespace && { namespace }),
|
|
@@ -48,12 +52,12 @@ export function ManagedCertificate(props: ManagedCertificateProps): ManagedCerti
|
|
|
48
52
|
managed: {
|
|
49
53
|
domains,
|
|
50
54
|
},
|
|
51
|
-
};
|
|
55
|
+
} as Record<string, unknown>, defs?.certificate));
|
|
52
56
|
|
|
53
|
-
const result:
|
|
57
|
+
const result: Record<string, any> = { certificate };
|
|
54
58
|
|
|
55
59
|
if (createProxy && backendServiceName) {
|
|
56
|
-
result.urlMap = {
|
|
60
|
+
result.urlMap = new URLMap(mergeDefaults({
|
|
57
61
|
metadata: {
|
|
58
62
|
name: `${name}-url-map`,
|
|
59
63
|
...(namespace && { namespace }),
|
|
@@ -62,9 +66,9 @@ export function ManagedCertificate(props: ManagedCertificateProps): ManagedCerti
|
|
|
62
66
|
defaultService: {
|
|
63
67
|
backendServiceRef: { name: backendServiceName },
|
|
64
68
|
},
|
|
65
|
-
};
|
|
69
|
+
} as Record<string, unknown>, defs?.urlMap));
|
|
66
70
|
|
|
67
|
-
result.targetHttpsProxy = {
|
|
71
|
+
result.targetHttpsProxy = new TargetHTTPSProxy(mergeDefaults({
|
|
68
72
|
metadata: {
|
|
69
73
|
name: `${name}-proxy`,
|
|
70
74
|
...(namespace && { namespace }),
|
|
@@ -72,8 +76,8 @@ export function ManagedCertificate(props: ManagedCertificateProps): ManagedCerti
|
|
|
72
76
|
},
|
|
73
77
|
urlMapRef: { name: `${name}-url-map` },
|
|
74
78
|
sslCertificates: [{ name }],
|
|
75
|
-
};
|
|
79
|
+
} as Record<string, unknown>, defs?.targetHttpsProxy));
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
return result;
|
|
79
|
-
}
|
|
83
|
+
}, "ManagedCertificate");
|
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
* PrivateService composite — GlobalAddress + ServiceNetworkingConnection + optional DNS.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
6
|
+
import {
|
|
7
|
+
ComputeAddress,
|
|
8
|
+
ServicenetworkingConnection,
|
|
9
|
+
DNSManagedZone,
|
|
10
|
+
} from "../generated";
|
|
11
|
+
|
|
5
12
|
export interface PrivateServiceProps {
|
|
6
13
|
/** Service name. */
|
|
7
14
|
name: string;
|
|
@@ -23,15 +30,15 @@ export interface PrivateServiceProps {
|
|
|
23
30
|
labels?: Record<string, string>;
|
|
24
31
|
/** Namespace for all resources. */
|
|
25
32
|
namespace?: string;
|
|
33
|
+
/** Per-member defaults for customizing individual resources. */
|
|
34
|
+
defaults?: {
|
|
35
|
+
globalAddress?: Partial<ConstructorParameters<typeof ComputeAddress>[0]>;
|
|
36
|
+
serviceConnection?: Partial<ConstructorParameters<typeof ServicenetworkingConnection>[0]>;
|
|
37
|
+
dnsZone?: Partial<ConstructorParameters<typeof DNSManagedZone>[0]>;
|
|
38
|
+
};
|
|
26
39
|
}
|
|
27
40
|
|
|
28
|
-
export
|
|
29
|
-
globalAddress: Record<string, unknown>;
|
|
30
|
-
serviceConnection: Record<string, unknown>;
|
|
31
|
-
dnsZone?: Record<string, unknown>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function PrivateService(props: PrivateServiceProps): PrivateServiceResult {
|
|
41
|
+
export const PrivateService = Composite<PrivateServiceProps>((props) => {
|
|
35
42
|
const {
|
|
36
43
|
name,
|
|
37
44
|
networkName,
|
|
@@ -43,6 +50,7 @@ export function PrivateService(props: PrivateServiceProps): PrivateServiceResult
|
|
|
43
50
|
dnsSuffix = "internal.",
|
|
44
51
|
labels: extraLabels = {},
|
|
45
52
|
namespace,
|
|
53
|
+
defaults: defs,
|
|
46
54
|
} = props;
|
|
47
55
|
|
|
48
56
|
const commonLabels: Record<string, string> = {
|
|
@@ -51,7 +59,7 @@ export function PrivateService(props: PrivateServiceProps): PrivateServiceResult
|
|
|
51
59
|
...extraLabels,
|
|
52
60
|
};
|
|
53
61
|
|
|
54
|
-
const globalAddress
|
|
62
|
+
const globalAddress = new ComputeAddress(mergeDefaults({
|
|
55
63
|
metadata: {
|
|
56
64
|
name: `${name}-address`,
|
|
57
65
|
...(namespace && { namespace }),
|
|
@@ -61,9 +69,9 @@ export function PrivateService(props: PrivateServiceProps): PrivateServiceResult
|
|
|
61
69
|
purpose,
|
|
62
70
|
prefixLength,
|
|
63
71
|
networkRef: { name: networkName },
|
|
64
|
-
};
|
|
72
|
+
} as Record<string, unknown>, defs?.globalAddress));
|
|
65
73
|
|
|
66
|
-
const serviceConnection
|
|
74
|
+
const serviceConnection = new ServicenetworkingConnection(mergeDefaults({
|
|
67
75
|
metadata: {
|
|
68
76
|
name: `${name}-connection`,
|
|
69
77
|
...(namespace && { namespace }),
|
|
@@ -72,12 +80,12 @@ export function PrivateService(props: PrivateServiceProps): PrivateServiceResult
|
|
|
72
80
|
networkRef: { name: networkName },
|
|
73
81
|
service: "servicenetworking.googleapis.com",
|
|
74
82
|
reservedPeeringRanges: [{ name: `${name}-address` }],
|
|
75
|
-
};
|
|
83
|
+
} as Record<string, unknown>, defs?.serviceConnection));
|
|
76
84
|
|
|
77
|
-
const result:
|
|
85
|
+
const result: Record<string, any> = { globalAddress, serviceConnection };
|
|
78
86
|
|
|
79
87
|
if (enableDns) {
|
|
80
|
-
result.dnsZone = {
|
|
88
|
+
result.dnsZone = new DNSManagedZone(mergeDefaults({
|
|
81
89
|
metadata: {
|
|
82
90
|
name: dnsZoneName ?? `${name}-dns`,
|
|
83
91
|
...(namespace && { namespace }),
|
|
@@ -88,8 +96,8 @@ export function PrivateService(props: PrivateServiceProps): PrivateServiceResult
|
|
|
88
96
|
privateVisibilityConfig: {
|
|
89
97
|
networks: [{ networkRef: { name: networkName } }],
|
|
90
98
|
},
|
|
91
|
-
};
|
|
99
|
+
} as Record<string, unknown>, defs?.dnsZone));
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
return result;
|
|
95
|
-
}
|
|
103
|
+
}, "PrivateService");
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* PubSubPipeline composite — Topic + Subscription + optional DLQ topic + subscriber IAM binding.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { Composite, mergeDefaults } from "@intentius/chant";
|
|
6
|
+
import { PubSubTopic, PubSubSubscription, IAMPolicyMember } from "../generated";
|
|
7
|
+
|
|
5
8
|
export interface PubSubPipelineProps {
|
|
6
9
|
/** Pipeline name. */
|
|
7
10
|
name: string;
|
|
@@ -19,16 +22,16 @@ export interface PubSubPipelineProps {
|
|
|
19
22
|
labels?: Record<string, string>;
|
|
20
23
|
/** Namespace for all resources. */
|
|
21
24
|
namespace?: string;
|
|
25
|
+
/** Per-member defaults for customizing individual resources. */
|
|
26
|
+
defaults?: {
|
|
27
|
+
topic?: Partial<ConstructorParameters<typeof PubSubTopic>[0]>;
|
|
28
|
+
subscription?: Partial<ConstructorParameters<typeof PubSubSubscription>[0]>;
|
|
29
|
+
deadLetterTopic?: Partial<ConstructorParameters<typeof PubSubTopic>[0]>;
|
|
30
|
+
subscriberIam?: Partial<ConstructorParameters<typeof IAMPolicyMember>[0]>;
|
|
31
|
+
};
|
|
22
32
|
}
|
|
23
33
|
|
|
24
|
-
export
|
|
25
|
-
topic: Record<string, unknown>;
|
|
26
|
-
subscription: Record<string, unknown>;
|
|
27
|
-
deadLetterTopic?: Record<string, unknown>;
|
|
28
|
-
subscriberIam?: Record<string, unknown>;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function PubSubPipeline(props: PubSubPipelineProps): PubSubPipelineResult {
|
|
34
|
+
export const PubSubPipeline = Composite<PubSubPipelineProps>((props) => {
|
|
32
35
|
const {
|
|
33
36
|
name,
|
|
34
37
|
ackDeadlineSeconds = 10,
|
|
@@ -38,6 +41,7 @@ export function PubSubPipeline(props: PubSubPipelineProps): PubSubPipelineResult
|
|
|
38
41
|
subscriberServiceAccount,
|
|
39
42
|
labels: extraLabels = {},
|
|
40
43
|
namespace,
|
|
44
|
+
defaults: defs,
|
|
41
45
|
} = props;
|
|
42
46
|
|
|
43
47
|
const commonLabels: Record<string, string> = {
|
|
@@ -46,15 +50,15 @@ export function PubSubPipeline(props: PubSubPipelineProps): PubSubPipelineResult
|
|
|
46
50
|
...extraLabels,
|
|
47
51
|
};
|
|
48
52
|
|
|
49
|
-
const topic
|
|
53
|
+
const topic = new PubSubTopic(mergeDefaults({
|
|
50
54
|
metadata: {
|
|
51
55
|
name: `${name}-topic`,
|
|
52
56
|
...(namespace && { namespace }),
|
|
53
57
|
labels: { ...commonLabels, "app.kubernetes.io/component": "topic" },
|
|
54
58
|
},
|
|
55
|
-
};
|
|
59
|
+
} as Record<string, unknown>, defs?.topic));
|
|
56
60
|
|
|
57
|
-
const
|
|
61
|
+
const subscriptionProps: Record<string, unknown> = {
|
|
58
62
|
metadata: {
|
|
59
63
|
name: `${name}-subscription`,
|
|
60
64
|
...(namespace && { namespace }),
|
|
@@ -65,24 +69,32 @@ export function PubSubPipeline(props: PubSubPipelineProps): PubSubPipelineResult
|
|
|
65
69
|
messageRetentionDuration,
|
|
66
70
|
};
|
|
67
71
|
|
|
68
|
-
const result:
|
|
72
|
+
const result: Record<string, any> = { topic };
|
|
69
73
|
|
|
70
74
|
if (enableDeadLetterQueue) {
|
|
71
|
-
result.deadLetterTopic = {
|
|
75
|
+
result.deadLetterTopic = new PubSubTopic(mergeDefaults({
|
|
72
76
|
metadata: {
|
|
73
77
|
name: `${name}-dlq`,
|
|
74
78
|
...(namespace && { namespace }),
|
|
75
79
|
labels: { ...commonLabels, "app.kubernetes.io/component": "dead-letter" },
|
|
76
80
|
},
|
|
77
|
-
};
|
|
78
|
-
|
|
81
|
+
} as Record<string, unknown>, defs?.deadLetterTopic));
|
|
82
|
+
|
|
83
|
+
subscriptionProps.deadLetterPolicy = {
|
|
79
84
|
deadLetterTopicRef: { name: `${name}-dlq` },
|
|
80
85
|
maxDeliveryAttempts,
|
|
81
86
|
};
|
|
82
87
|
}
|
|
83
88
|
|
|
89
|
+
const subscription = new PubSubSubscription(mergeDefaults(
|
|
90
|
+
subscriptionProps,
|
|
91
|
+
defs?.subscription,
|
|
92
|
+
));
|
|
93
|
+
|
|
94
|
+
result.subscription = subscription;
|
|
95
|
+
|
|
84
96
|
if (subscriberServiceAccount) {
|
|
85
|
-
result.subscriberIam = {
|
|
97
|
+
result.subscriberIam = new IAMPolicyMember(mergeDefaults({
|
|
86
98
|
metadata: {
|
|
87
99
|
name: `${name}-subscriber`,
|
|
88
100
|
...(namespace && { namespace }),
|
|
@@ -95,8 +107,8 @@ export function PubSubPipeline(props: PubSubPipelineProps): PubSubPipelineResult
|
|
|
95
107
|
kind: "PubSubSubscription",
|
|
96
108
|
name: `${name}-subscription`,
|
|
97
109
|
},
|
|
98
|
-
};
|
|
110
|
+
} as Record<string, unknown>, defs?.subscriberIam));
|
|
99
111
|
}
|
|
100
112
|
|
|
101
113
|
return result;
|
|
102
|
-
}
|
|
114
|
+
}, "PubSubPipeline");
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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:
|
|
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");
|