@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.
@@ -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
- export interface GkeClusterResult {
34
- cluster: Record<string, unknown>;
35
- nodePool: Record<string, unknown>;
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 prop objects for
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 function GkeCluster(props: GkeClusterProps): GkeClusterResult {
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
- initialNodeCount: 1, // Minimal default pool, real nodes in separate pool
76
- removeDefaultNodePool: true,
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: `PROJECT_ID.svc.id.goog`,
141
+ workloadPool: `${projectId}.svc.id.goog`,
84
142
  };
85
143
  }
86
144
 
87
- const cluster: Record<string, unknown> = {
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: Record<string, unknown> = {
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
- return { cluster, nodePool };
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");
@@ -1,20 +1,22 @@
1
1
  export { GkeCluster } from "./gke-cluster";
2
- export type { GkeClusterProps, GkeClusterResult } from "./gke-cluster";
3
- export { CloudRunService } from "./cloud-run-service";
4
- export type { CloudRunServiceProps, CloudRunServiceResult } from "./cloud-run-service";
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, CloudSqlInstanceResult } from "./cloud-sql-instance";
6
+ export type { CloudSqlInstanceProps } from "./cloud-sql-instance";
7
7
  export { GcsBucket } from "./gcs-bucket";
8
- export type { GcsBucketProps, GcsBucketResult } from "./gcs-bucket";
8
+ export type { GcsBucketProps } from "./gcs-bucket";
9
9
  export { VpcNetwork } from "./vpc-network";
10
- export type { VpcNetworkProps, VpcNetworkResult, VpcSubnet } from "./vpc-network";
10
+ export type { VpcNetworkProps, VpcSubnet } from "./vpc-network";
11
11
  export { PubSubPipeline } from "./pubsub-pipeline";
12
- export type { PubSubPipelineProps, PubSubPipelineResult } from "./pubsub-pipeline";
12
+ export type { PubSubPipelineProps } from "./pubsub-pipeline";
13
13
  export { CloudFunctionWithTrigger } from "./cloud-function";
14
- export type { CloudFunctionWithTriggerProps, CloudFunctionWithTriggerResult } from "./cloud-function";
14
+ export type { CloudFunctionWithTriggerProps } from "./cloud-function";
15
15
  export { PrivateService } from "./private-service";
16
- export type { PrivateServiceProps, PrivateServiceResult } from "./private-service";
16
+ export type { PrivateServiceProps } from "./private-service";
17
17
  export { ManagedCertificate } from "./managed-certificate";
18
- export type { ManagedCertificateProps, ManagedCertificateResult } from "./managed-certificate";
18
+ export type { ManagedCertificateProps } from "./managed-certificate";
19
19
  export { SecureProject } from "./secure-project";
20
- export type { SecureProjectProps, SecureProjectResult } from "./secure-project";
20
+ export type { SecureProjectProps } from "./secure-project";
21
+ export { MemorystoreRedis } from "./memorystore-redis";
22
+ export type { MemorystoreRedisProps } from "./memorystore-redis";
@@ -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 interface ManagedCertificateResult {
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: Record<string, unknown> = {
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: ManagedCertificateResult = { certificate };
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");
@@ -0,0 +1,101 @@
1
+ /**
2
+ * MemorystoreRedis composite — RedisInstance with purpose-driven defaults.
3
+ */
4
+
5
+ import { Composite, mergeDefaults } from "@intentius/chant";
6
+ import { RedisInstance } from "../generated";
7
+
8
+ export interface MemorystoreRedisProps {
9
+ /** Instance name. */
10
+ name: string;
11
+ /**
12
+ * Purpose drives the maxmemory-policy default:
13
+ * - "persistent" → "noeviction" (queues, shared_state must not evict)
14
+ * - "cache" → "allkeys-lru" (cache/sessions can evict LRU)
15
+ */
16
+ purpose: "persistent" | "cache";
17
+ /** Memorystore tier (e.g. "BASIC", "STANDARD_HA"). */
18
+ tier: string;
19
+ /** Memory size in GB. */
20
+ memorySizeGb: number;
21
+ /** GCP region. */
22
+ region: string;
23
+ /** authorizedNetworkRef.name — the VPC network to authorize. */
24
+ networkRef: string;
25
+ /** Redis version (default: "REDIS_7_0"). */
26
+ redisVersion?: string;
27
+ /** Enable AUTH (default: true). */
28
+ authEnabled?: boolean;
29
+ /** Namespace for all resources. */
30
+ namespace?: string;
31
+ /** Additional labels. */
32
+ labels?: Record<string, string>;
33
+ /** Per-member defaults for customizing individual resources. */
34
+ defaults?: {
35
+ instance?: Partial<Record<string, unknown>>;
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Create a MemorystoreRedis composite.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * import { MemorystoreRedis } from "@intentius/chant-lexicon-gcp";
45
+ *
46
+ * const { instance } = MemorystoreRedis({
47
+ * name: "my-app-persistent",
48
+ * purpose: "persistent",
49
+ * tier: "STANDARD_HA",
50
+ * memorySizeGb: 5,
51
+ * region: "us-central1",
52
+ * networkRef: "my-vpc",
53
+ * });
54
+ * ```
55
+ */
56
+ export const MemorystoreRedis = Composite<MemorystoreRedisProps>((props) => {
57
+ const {
58
+ name,
59
+ purpose,
60
+ tier,
61
+ memorySizeGb,
62
+ region,
63
+ networkRef,
64
+ redisVersion = "REDIS_7_0",
65
+ authEnabled = true,
66
+ namespace,
67
+ labels: extraLabels = {},
68
+ defaults: defs,
69
+ } = props;
70
+
71
+ const maxmemoryPolicy = purpose === "persistent" ? "noeviction" : "allkeys-lru";
72
+
73
+ const commonLabels: Record<string, string> = {
74
+ "app.kubernetes.io/name": name,
75
+ "app.kubernetes.io/managed-by": "chant",
76
+ ...extraLabels,
77
+ };
78
+
79
+ const instance = new RedisInstance(mergeDefaults({
80
+ metadata: {
81
+ name,
82
+ ...(namespace && { namespace }),
83
+ labels: { ...commonLabels, "app.kubernetes.io/component": "cache" },
84
+ },
85
+ region,
86
+ tier,
87
+ memorySizeGb,
88
+ authEnabled,
89
+ redisVersion,
90
+ connectMode: "PRIVATE_SERVICE_ACCESS",
91
+ authorizedNetworkRef: { name: networkRef },
92
+ // TLS disabled: GitLab's redis-rb client requires either no TLS or a custom CA cert
93
+ // injected via REDIS_SSL_CA_FILE. AUTH (authEnabled: true) provides access control.
94
+ transitEncryptionMode: "DISABLED",
95
+ redisConfigs: {
96
+ "maxmemory-policy": maxmemoryPolicy,
97
+ },
98
+ } as Record<string, unknown>, defs?.instance));
99
+
100
+ return { instance };
101
+ }, "MemorystoreRedis");
@@ -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 interface PrivateServiceResult {
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: Record<string, unknown> = {
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: Record<string, unknown> = {
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: PrivateServiceResult = { globalAddress, serviceConnection };
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 interface PubSubPipelineResult {
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: Record<string, unknown> = {
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 subscription: Record<string, unknown> = {
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: PubSubPipelineResult = { topic, subscription };
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
- subscription.deadLetterPolicy = {
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");