@crossdelta/infrastructure 0.6.2 → 0.6.4
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/README.md +59 -599
- package/dist/index.cjs +23 -16
- package/dist/index.js +23 -16
- package/dist/runtimes/doks/probes.d.ts +32 -0
- package/dist/runtimes/doks/types.d.ts +3 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,670 +3,130 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@crossdelta/infrastructure)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
**Current Support:**
|
|
9
|
-
- ✅ **DigitalOcean Kubernetes (DOKS)** - Production-ready, fully tested
|
|
10
|
-
- ✅ **Local Kubernetes (k3d)** - Development environment
|
|
11
|
-
|
|
12
|
-
**Planned Support:**
|
|
13
|
-
- 🚧 **AWS EKS** - Planned (contributions welcome)
|
|
14
|
-
- 🚧 **Azure AKS** - Planned (contributions welcome)
|
|
15
|
-
- 🚧 **Google GKE** - Planned (contributions welcome)
|
|
16
|
-
|
|
17
|
-
The architecture is **provider-agnostic** - runtime components (NATS, Ingress, Cert-Manager) use provider-specific implementations, but service configs remain portable.
|
|
18
|
-
|
|
19
|
-
## Architecture Overview
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
┌─────────────────────────────────────────┐
|
|
23
|
-
│ DigitalOcean Kubernetes (DOKS) │
|
|
24
|
-
│ │
|
|
25
|
-
┌───────────────┤ Runtime Components: │
|
|
26
|
-
│ │ • NATS + JetStream (messaging) │
|
|
27
|
-
Internet │ • NGINX Ingress (routing) │
|
|
28
|
-
│ │ • Cert-Manager (TLS certs) │
|
|
29
|
-
│ └─────────────────────────────────────────┘
|
|
30
|
-
▼ │
|
|
31
|
-
┌──────────┐ ▼
|
|
32
|
-
│ DNS │ ┌──────────────────────────────────────────┐
|
|
33
|
-
└────┬─────┘ │ Microservices: │
|
|
34
|
-
│ │ │
|
|
35
|
-
▼ │ ┌──────────────┐ ┌──────────────┐ │
|
|
36
|
-
┌──────────┐ │ │ api-gateway │ │ orders │ │
|
|
37
|
-
│ Load │◄────────┼──┤ :4000 │ │ :4001 │ │
|
|
38
|
-
│ Balancer │ │ └──────┬───────┘ └──────┬───────┘ │
|
|
39
|
-
└──────────┘ │ │ │ │
|
|
40
|
-
│ ▼ ▼ │
|
|
41
|
-
│ ┌──────────────────────────────┐ │
|
|
42
|
-
│ │ NATS (Event Stream) │ │
|
|
43
|
-
│ └──────────────────────────────┘ │
|
|
44
|
-
│ ▲ ▲ │
|
|
45
|
-
│ │ │ │
|
|
46
|
-
│ ┌──────┴───────┐ ┌──────┴───────┐ │
|
|
47
|
-
│ │notifications │ │ storefront │ │
|
|
48
|
-
│ │ :4002 │ │ :3000 │ │
|
|
49
|
-
│ └──────────────┘ └──────────────┘ │
|
|
50
|
-
└──────────────────────────────────────────┘
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### Why Kubernetes?
|
|
54
|
-
|
|
55
|
-
| Feature | Benefit |
|
|
56
|
-
|---------|---------|
|
|
57
|
-
| **Declarative** | Define desired state, K8s maintains it |
|
|
58
|
-
| **Self-healing** | Auto-restart failed pods, reschedule on node failure |
|
|
59
|
-
| **Scaling** | Horizontal pod autoscaling based on metrics |
|
|
60
|
-
| **Rolling updates** | Zero-downtime deployments |
|
|
61
|
-
| **Service discovery** | Built-in DNS for pod-to-pod communication |
|
|
62
|
-
| **Persistent volumes** | StatefulSets for databases and message queues |
|
|
63
|
-
|
|
64
|
-
## Features
|
|
65
|
-
|
|
66
|
-
### 🚀 Core Features
|
|
67
|
-
- **Provider-Agnostic** - One config for DOKS, k3d, EKS, AKS, GKE
|
|
68
|
-
- **Unified Runtime API** - Single `deployRuntime()` for NATS, Ingress, Cert-Manager
|
|
69
|
-
- **Service Discovery** - Auto-discover services from `infra/services/*.ts`
|
|
70
|
-
- **Fluent Port Configuration** - Modern API for HTTP, gRPC, custom ports
|
|
71
|
-
- **Health Checks** - Automatic HTTP health check configuration
|
|
72
|
-
- **Type-Safe** - Full TypeScript support with strict typing
|
|
73
|
-
|
|
74
|
-
### 📦 Runtime Components
|
|
75
|
-
- **NATS + JetStream** - Event-driven messaging with persistence
|
|
76
|
-
- **NGINX Ingress** - HTTP routing and load balancing
|
|
77
|
-
- **Cert-Manager** - Automatic TLS certificates via Let's Encrypt
|
|
78
|
-
- **OpenTelemetry** - Distributed tracing and metrics (via `@crossdelta/telemetry`)
|
|
79
|
-
|
|
80
|
-
### 🔧 Developer Experience
|
|
81
|
-
- **Auto-Generated Types** - Service names from directory structure
|
|
82
|
-
- **Environment Generation** - `.env.local` from Pulumi secrets
|
|
83
|
-
- **Image Registry Support** - GHCR, DockerHub, DOCR
|
|
84
|
-
- **Health Check Integration** - Kubernetes liveness/readiness probes
|
|
6
|
+
Opinionated Pulumi abstractions for deploying microservices to Kubernetes. Turns per-service config objects into Deployments, Services, Ingress, probes, and secrets.
|
|
85
7
|
|
|
86
8
|
## Installation
|
|
87
9
|
|
|
88
10
|
```bash
|
|
89
|
-
npm install @crossdelta/infrastructure
|
|
11
|
+
npm install @crossdelta/infrastructure @pulumi/pulumi @pulumi/kubernetes
|
|
90
12
|
```
|
|
91
13
|
|
|
92
|
-
|
|
14
|
+
## Usage
|
|
93
15
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
# Required - Pulumi core and Kubernetes
|
|
98
|
-
npm install @pulumi/pulumi @pulumi/kubernetes
|
|
99
|
-
|
|
100
|
-
# Optional - DigitalOcean provider (for DOKS)
|
|
101
|
-
npm install @pulumi/digitalocean
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
| Package | Required | Description |
|
|
105
|
-
|---------|----------|-------------|
|
|
106
|
-
| `@pulumi/pulumi` | ✅ Yes | Pulumi core SDK |
|
|
107
|
-
| `@pulumi/kubernetes` | ✅ Yes | Kubernetes resources |
|
|
108
|
-
| `@pulumi/digitalocean` | Optional | DigitalOcean provider (DOKS, VPC, etc.) |
|
|
109
|
-
|
|
110
|
-
## Quick Start
|
|
111
|
-
|
|
112
|
-
### 1. Create service configs
|
|
16
|
+
### Service config
|
|
113
17
|
|
|
114
18
|
```typescript
|
|
115
|
-
// infra/services/orders.ts
|
|
116
19
|
import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
|
|
117
20
|
|
|
118
21
|
const config: K8sServiceConfig = {
|
|
119
|
-
name: '
|
|
120
|
-
ports: ports().http(
|
|
22
|
+
name: 'my-service',
|
|
23
|
+
ports: ports().http(4000).public().build(),
|
|
121
24
|
replicas: 1,
|
|
122
|
-
healthCheck: {
|
|
25
|
+
healthCheck: {
|
|
26
|
+
httpPath: '/health',
|
|
27
|
+
readinessPath: '/health/ready',
|
|
28
|
+
},
|
|
29
|
+
ingress: { path: '/api', host: 'example.com' },
|
|
30
|
+
env: { DATABASE_URL: dbUrl },
|
|
31
|
+
secrets: { API_KEY: apiKey },
|
|
123
32
|
resources: {
|
|
124
33
|
requests: { cpu: '50m', memory: '64Mi' },
|
|
125
|
-
limits: { cpu: '
|
|
126
|
-
},
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export default config
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
// infra/services/api-gateway.ts - Public service with ingress
|
|
134
|
-
import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
|
|
135
|
-
|
|
136
|
-
const config: K8sServiceConfig = {
|
|
137
|
-
name: 'api-gateway',
|
|
138
|
-
ports: ports().http(4000).public().build(),
|
|
139
|
-
ingress: {
|
|
140
|
-
path: '/api',
|
|
141
|
-
host: 'api.example.com',
|
|
34
|
+
limits: { cpu: '200m', memory: '256Mi' },
|
|
142
35
|
},
|
|
143
|
-
replicas: 2,
|
|
144
|
-
healthCheck: { httpPath: '/health' },
|
|
145
36
|
}
|
|
146
37
|
|
|
147
38
|
export default config
|
|
148
39
|
```
|
|
149
40
|
|
|
150
|
-
###
|
|
41
|
+
### Deploy services
|
|
151
42
|
|
|
152
43
|
```typescript
|
|
153
|
-
|
|
154
|
-
import {
|
|
155
|
-
createDOKSCluster,
|
|
156
|
-
createNamespace,
|
|
157
|
-
createVPC,
|
|
158
|
-
deployK8sServices,
|
|
159
|
-
deployRuntime,
|
|
160
|
-
discoverServiceConfigs,
|
|
161
|
-
} from '@crossdelta/infrastructure'
|
|
44
|
+
import { deployK8sServices, discoverServiceConfigs } from '@crossdelta/infrastructure'
|
|
162
45
|
|
|
163
|
-
const
|
|
164
|
-
const namespace = 'my-platform'
|
|
46
|
+
const serviceConfigs = discoverServiceConfigs('services')
|
|
165
47
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
48
|
+
deployK8sServices({
|
|
49
|
+
provider,
|
|
50
|
+
namespace,
|
|
51
|
+
serviceConfigs,
|
|
52
|
+
env: { NATS_URL: natsUrl },
|
|
171
53
|
})
|
|
54
|
+
```
|
|
172
55
|
|
|
173
|
-
|
|
174
|
-
const { cluster, provider, kubeconfig } = createDOKSCluster({
|
|
175
|
-
name: `${namespace}-cluster`,
|
|
176
|
-
region,
|
|
177
|
-
vpcUuid: vpc.id,
|
|
178
|
-
nodePool: {
|
|
179
|
-
name: 'default',
|
|
180
|
-
size: 's-2vcpu-4gb',
|
|
181
|
-
nodeCount: 2,
|
|
182
|
-
},
|
|
183
|
-
})
|
|
56
|
+
### Deploy runtime components
|
|
184
57
|
|
|
185
|
-
|
|
186
|
-
|
|
58
|
+
```typescript
|
|
59
|
+
import { deployRuntime } from '@crossdelta/infrastructure'
|
|
187
60
|
|
|
188
|
-
// 4. Deploy runtime components (NATS, Ingress, Cert-Manager)
|
|
189
61
|
const runtime = deployRuntime(provider, namespace, {
|
|
190
|
-
nats: {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
replicas: 1,
|
|
194
|
-
jetstream: {
|
|
195
|
-
enabled: true,
|
|
196
|
-
storageSize: '1Gi',
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
ingress: {
|
|
201
|
-
enabled: true,
|
|
202
|
-
config: { replicas: 1 },
|
|
203
|
-
},
|
|
204
|
-
certManager: {
|
|
205
|
-
enabled: true,
|
|
206
|
-
config: { email: 'admin@example.com' },
|
|
207
|
-
},
|
|
62
|
+
nats: { enabled: true, config: { replicas: 1, jetstream: { enabled: true } } },
|
|
63
|
+
ingress: { enabled: true },
|
|
64
|
+
certManager: { enabled: true, config: { email: 'ops@example.com' } },
|
|
208
65
|
})
|
|
209
|
-
|
|
210
|
-
// 5. Auto-discover and deploy services
|
|
211
|
-
const serviceConfigs = discoverServiceConfigs('services')
|
|
212
|
-
const deployedServices = deployK8sServices({
|
|
213
|
-
provider,
|
|
214
|
-
namespace,
|
|
215
|
-
serviceConfigs,
|
|
216
|
-
env: {
|
|
217
|
-
NATS_URL: runtime.natsUrl,
|
|
218
|
-
},
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
// Export outputs
|
|
222
|
-
export const clusterEndpoint = cluster.endpoint
|
|
223
|
-
export const clusterKubeconfig = kubeconfig
|
|
224
|
-
export const natsInternalUrl = runtime.natsUrl
|
|
225
|
-
export const loadBalancerIP = runtime.loadBalancerIp
|
|
226
66
|
```
|
|
227
67
|
|
|
228
|
-
## K8sServiceConfig
|
|
229
|
-
|
|
230
|
-
The `K8sServiceConfig` type is the central configuration for Kubernetes services:
|
|
68
|
+
## K8sServiceConfig
|
|
231
69
|
|
|
232
70
|
```typescript
|
|
233
71
|
interface K8sServiceConfig {
|
|
234
|
-
/** Service name (used for deployment, service, ingress names) */
|
|
235
72
|
name: string
|
|
236
|
-
|
|
237
|
-
/** Port configuration (use fluent ports() builder) */
|
|
238
73
|
ports: PortConfig
|
|
239
|
-
|
|
240
|
-
/** Number of pod replicas (default: 1) */
|
|
241
74
|
replicas?: number
|
|
242
|
-
|
|
243
|
-
/** Resource requests and limits */
|
|
244
75
|
resources?: {
|
|
245
76
|
requests?: { cpu: string; memory: string }
|
|
246
77
|
limits?: { cpu: string; memory: string }
|
|
247
78
|
}
|
|
248
|
-
|
|
249
|
-
/** Health check configuration */
|
|
250
79
|
healthCheck?: {
|
|
251
|
-
httpPath: string
|
|
252
|
-
|
|
253
|
-
|
|
80
|
+
httpPath: string
|
|
81
|
+
readinessPath?: string // Falls back to httpPath
|
|
82
|
+
initialDelaySeconds?: number // Default: 10
|
|
83
|
+
periodSeconds?: number // Default: 10
|
|
84
|
+
failureThreshold?: number // Default: 3
|
|
254
85
|
}
|
|
255
|
-
|
|
256
|
-
/** Ingress configuration for public services */
|
|
257
86
|
ingress?: {
|
|
258
|
-
path: string
|
|
259
|
-
host?: string
|
|
87
|
+
path: string
|
|
88
|
+
host?: string
|
|
260
89
|
}
|
|
261
|
-
|
|
262
|
-
/** Environment variables */
|
|
263
90
|
env?: Record<string, pulumi.Input<string>>
|
|
264
|
-
|
|
265
|
-
/** Kubernetes secrets */
|
|
266
91
|
secrets?: Record<string, pulumi.Input<string>>
|
|
267
|
-
|
|
268
|
-
/** Custom image (defaults to GHCR with auto-generated tag) */
|
|
269
92
|
image?: string
|
|
270
|
-
|
|
271
|
-
/** Skip deployment */
|
|
272
93
|
skip?: boolean
|
|
273
94
|
}
|
|
274
95
|
```
|
|
275
96
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
Build port configurations with a fluent, chainable API:
|
|
97
|
+
## Fluent Ports API
|
|
279
98
|
|
|
280
99
|
```typescript
|
|
281
100
|
import { ports } from '@crossdelta/infrastructure'
|
|
282
101
|
|
|
283
|
-
// Simple HTTP
|
|
284
|
-
ports().http(4000).build()
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
ports().http(4000).public().build()
|
|
288
|
-
|
|
289
|
-
// gRPC service
|
|
290
|
-
ports().grpc(50051).build()
|
|
291
|
-
|
|
292
|
-
// Multiple ports
|
|
293
|
-
ports()
|
|
294
|
-
.http(4000) // Primary port
|
|
295
|
-
.addHttp(8080, 'metrics').public() // Additional public port
|
|
296
|
-
.add(9090, 'admin') // Additional internal port
|
|
297
|
-
.build()
|
|
298
|
-
|
|
299
|
-
// Custom protocol
|
|
300
|
-
ports()
|
|
301
|
-
.primary(4222, 'client')
|
|
302
|
-
.protocol('TCP')
|
|
303
|
-
.add(8222, 'monitoring')
|
|
304
|
-
.build()
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
**Port Builder Methods:**
|
|
308
|
-
- `.http(port)` - HTTP primary port
|
|
309
|
-
- `.https(port)` - HTTPS primary port
|
|
310
|
-
- `.grpc(port)` - gRPC primary port
|
|
311
|
-
- `.primary(port, name?)` - Custom primary port
|
|
312
|
-
- `.add(port, name?, protocol?)` - Add additional port
|
|
313
|
-
- `.addHttp(port, name?)` - Add HTTP additional port
|
|
314
|
-
- `.addGrpc(port, name?)` - Add gRPC additional port
|
|
315
|
-
- `.public()` - Mark last port as public (exposed via ingress)
|
|
316
|
-
- `.protocol(type)` - Set protocol for last port
|
|
317
|
-
- `.build()` - Build final config
|
|
318
|
-
|
|
319
|
-
## Service Configuration Examples
|
|
320
|
-
|
|
321
|
-
### Public HTTP Service with Ingress
|
|
322
|
-
|
|
323
|
-
```typescript
|
|
324
|
-
import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
|
|
325
|
-
|
|
326
|
-
const config: K8sServiceConfig = {
|
|
327
|
-
name: 'api-gateway',
|
|
328
|
-
ports: ports().http(4000).public().build(),
|
|
329
|
-
replicas: 2,
|
|
330
|
-
healthCheck: { httpPath: '/health' },
|
|
331
|
-
ingress: {
|
|
332
|
-
path: '/api',
|
|
333
|
-
host: 'api.example.com',
|
|
334
|
-
},
|
|
335
|
-
resources: {
|
|
336
|
-
requests: { cpu: '100m', memory: '128Mi' },
|
|
337
|
-
limits: { cpu: '500m', memory: '512Mi' },
|
|
338
|
-
},
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
export default config
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### Internal Service (Event Consumer)
|
|
345
|
-
|
|
346
|
-
```typescript
|
|
347
|
-
import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
|
|
348
|
-
|
|
349
|
-
const config: K8sServiceConfig = {
|
|
350
|
-
name: 'notifications',
|
|
351
|
-
ports: ports().http(4002).build(),
|
|
352
|
-
replicas: 1,
|
|
353
|
-
healthCheck: { httpPath: '/health' },
|
|
354
|
-
resources: {
|
|
355
|
-
requests: { cpu: '50m', memory: '64Mi' },
|
|
356
|
-
limits: { cpu: '150m', memory: '128Mi' },
|
|
357
|
-
},
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
export default config
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
### Service with Multiple Ports
|
|
364
|
-
|
|
365
|
-
```typescript
|
|
366
|
-
import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
|
|
367
|
-
|
|
368
|
-
const config: K8sServiceConfig = {
|
|
369
|
-
name: 'api-gateway',
|
|
370
|
-
ports: ports()
|
|
371
|
-
.http(4000) // Primary HTTP port
|
|
372
|
-
.addHttp(8080, 'metrics').public() // Metrics endpoint (public)
|
|
373
|
-
.add(9090, 'admin') // Admin endpoint (internal)
|
|
374
|
-
.build(),
|
|
375
|
-
replicas: 2,
|
|
376
|
-
healthCheck: { httpPath: '/health' },
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
export default config
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
### gRPC Service
|
|
383
|
-
|
|
384
|
-
```typescript
|
|
385
|
-
import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
|
|
386
|
-
|
|
387
|
-
const config: K8sServiceConfig = {
|
|
388
|
-
name: 'grpc-service',
|
|
389
|
-
ports: ports().grpc(50051).build(),
|
|
390
|
-
replicas: 2,
|
|
391
|
-
healthCheck: {
|
|
392
|
-
httpPath: '/grpc.health.v1.Health/Check',
|
|
393
|
-
initialDelaySeconds: 15,
|
|
394
|
-
},
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
export default config
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
## deployRuntime() - Unified Runtime Components
|
|
401
|
-
|
|
402
|
-
Deploy all runtime components (NATS, Ingress, Cert-Manager) with a single function:
|
|
403
|
-
|
|
404
|
-
```typescript
|
|
405
|
-
import { deployRuntime } from '@crossdelta/infrastructure'
|
|
406
|
-
|
|
407
|
-
const runtime = deployRuntime(provider, namespace, {
|
|
408
|
-
// NATS with JetStream for event-driven messaging
|
|
409
|
-
nats: {
|
|
410
|
-
enabled: true,
|
|
411
|
-
config: {
|
|
412
|
-
replicas: 1,
|
|
413
|
-
jetstream: {
|
|
414
|
-
enabled: true,
|
|
415
|
-
storageSize: '1Gi',
|
|
416
|
-
storageClass: 'do-block-storage',
|
|
417
|
-
},
|
|
418
|
-
resources: {
|
|
419
|
-
requests: { cpu: '25m', memory: '64Mi' },
|
|
420
|
-
limits: { cpu: '100m', memory: '192Mi' },
|
|
421
|
-
},
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
|
-
|
|
425
|
-
// NGINX Ingress Controller for HTTP routing
|
|
426
|
-
ingress: {
|
|
427
|
-
enabled: true,
|
|
428
|
-
config: {
|
|
429
|
-
replicas: 1,
|
|
430
|
-
resources: {
|
|
431
|
-
requests: { cpu: '50m', memory: '64Mi' },
|
|
432
|
-
limits: { cpu: '100m', memory: '128Mi' },
|
|
433
|
-
},
|
|
434
|
-
},
|
|
435
|
-
},
|
|
436
|
-
|
|
437
|
-
// Cert-Manager for automatic TLS certificates
|
|
438
|
-
certManager: {
|
|
439
|
-
enabled: true,
|
|
440
|
-
config: {
|
|
441
|
-
email: 'admin@example.com',
|
|
442
|
-
staging: false, // Use Let's Encrypt production
|
|
443
|
-
resources: {
|
|
444
|
-
requests: { cpu: '10m', memory: '32Mi' },
|
|
445
|
-
limits: { cpu: '100m', memory: '128Mi' },
|
|
446
|
-
},
|
|
447
|
-
},
|
|
448
|
-
},
|
|
449
|
-
})
|
|
450
|
-
|
|
451
|
-
// Use runtime outputs
|
|
452
|
-
console.log('NATS URL:', runtime.natsUrl)
|
|
453
|
-
console.log('LoadBalancer IP:', runtime.loadBalancerIp)
|
|
454
|
-
console.log('Cert-Manager Ready:', runtime.certManagerReady)
|
|
102
|
+
ports().http(4000).build() // Simple HTTP
|
|
103
|
+
ports().http(4000).public().build() // Public (ingress)
|
|
104
|
+
ports().grpc(50051).build() // gRPC
|
|
105
|
+
ports().http(4000).addHttp(8080, 'metrics').build() // Multiple ports
|
|
455
106
|
```
|
|
456
107
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
Generate `.env.local` for local development from Pulumi config:
|
|
469
|
-
|
|
470
|
-
```bash
|
|
471
|
-
# In your workspace root
|
|
472
|
-
bun dev # Automatically generates .env.local before starting services
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
**Generated `.env.local`:**
|
|
476
|
-
```bash
|
|
477
|
-
# Auto-generated from Pulumi config (DO NOT EDIT)
|
|
478
|
-
ORDERS_PORT=4001
|
|
479
|
-
NOTIFICATIONS_PORT=4002
|
|
480
|
-
API_GATEWAY_PORT=4000
|
|
481
|
-
STOREFRONT_PORT=3000
|
|
482
|
-
|
|
483
|
-
NATS_URL=nats://localhost:4222
|
|
484
|
-
DATABASE_URL=postgresql://localhost:5432/mydb
|
|
485
|
-
API_KEY=your-secret-key
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
**Generated `infra/env.ts`:**
|
|
489
|
-
```typescript
|
|
490
|
-
// Auto-generated type-safe service names
|
|
491
|
-
export type ServiceName = 'orders' | 'notifications' | 'api-gateway' | 'storefront'
|
|
492
|
-
|
|
493
|
-
export function getServicePort(name: ServiceName, fallback?: number): number {
|
|
494
|
-
const port = process.env[`${name.toUpperCase().replace(/-/g, '_')}_PORT`]
|
|
495
|
-
return port ? Number.parseInt(port, 10) : (fallback ?? 8080)
|
|
496
|
-
}
|
|
497
|
-
```
|
|
108
|
+
| Method | Description |
|
|
109
|
+
|--------|-------------|
|
|
110
|
+
| `.http(port)` | HTTP primary port |
|
|
111
|
+
| `.grpc(port)` | gRPC primary port |
|
|
112
|
+
| `.primary(port, name?)` | Custom primary port |
|
|
113
|
+
| `.add(port, name?, protocol?)` | Additional port |
|
|
114
|
+
| `.addHttp(port, name?)` | Additional HTTP port |
|
|
115
|
+
| `.addGrpc(port, name?)` | Additional gRPC port |
|
|
116
|
+
| `.public()` | Expose via ingress |
|
|
117
|
+
| `.protocol(type)` | Set protocol (TCP, UDP, SCTP) |
|
|
118
|
+
| `.build()` | Build config |
|
|
498
119
|
|
|
499
120
|
## API Reference
|
|
500
121
|
|
|
501
|
-
### Core Functions
|
|
502
|
-
|
|
503
122
|
| Function | Description |
|
|
504
123
|
|----------|-------------|
|
|
505
|
-
| `deployRuntime(provider,
|
|
506
|
-
| `discoverServiceConfigs(dir,
|
|
507
|
-
| `deployK8sServices(
|
|
508
|
-
| `
|
|
509
|
-
| `
|
|
510
|
-
| `
|
|
511
|
-
| `createImagePullSecret(provider, namespace, name, config)` | Create secret for private registries |
|
|
512
|
-
|
|
513
|
-
### Port Configuration
|
|
514
|
-
|
|
515
|
-
| Function | Description |
|
|
516
|
-
|----------|-------------|
|
|
517
|
-
| `ports()` | Start fluent port builder |
|
|
518
|
-
| `.http(port)` | Set HTTP primary port |
|
|
519
|
-
| `.https(port)` | Set HTTPS primary port |
|
|
520
|
-
| `.grpc(port)` | Set gRPC primary port |
|
|
521
|
-
| `.primary(port, name?)` | Set custom primary port |
|
|
522
|
-
| `.add(port, name?, protocol?)` | Add additional port |
|
|
523
|
-
| `.addHttp(port, name?)` | Add HTTP additional port |
|
|
524
|
-
| `.addGrpc(port, name?)` | Add gRPC additional port |
|
|
525
|
-
| `.public()` | Mark last port as public (ingress) |
|
|
526
|
-
| `.protocol(type)` | Set protocol ('TCP', 'UDP', 'SCTP') |
|
|
527
|
-
| `.build()` | Build final port configuration |
|
|
528
|
-
|
|
529
|
-
### Service Discovery Options
|
|
530
|
-
|
|
531
|
-
```typescript
|
|
532
|
-
discoverServiceConfigs('services', {
|
|
533
|
-
filter?: RegExp | string, // Filter by service name pattern
|
|
534
|
-
include?: string[], // Whitelist specific services
|
|
535
|
-
exclude?: string[], // Blacklist specific services
|
|
536
|
-
tags?: string[], // Filter by tags
|
|
537
|
-
env?: Record<string, string>, // Inject env vars
|
|
538
|
-
validate?: boolean, // Validate configs (default: true)
|
|
539
|
-
sort?: boolean, // Sort by name (default: true)
|
|
540
|
-
})
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
### Kubernetes Deployment Options
|
|
544
|
-
|
|
545
|
-
```typescript
|
|
546
|
-
deployK8sServices({
|
|
547
|
-
provider: k8s.Provider, // Kubernetes provider
|
|
548
|
-
namespace: string, // Target namespace
|
|
549
|
-
serviceConfigs: K8sServiceConfig[], // Service configurations
|
|
550
|
-
env?: Record<string, pulumi.Input<string>>, // Global env vars
|
|
551
|
-
secrets?: Record<string, pulumi.Input<string>>, // Global secrets
|
|
552
|
-
imagePullSecrets?: string[], // Image pull secret names
|
|
553
|
-
registry?: string, // Override registry URL
|
|
554
|
-
})
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
Returns a Map:
|
|
558
|
-
```typescript
|
|
559
|
-
Map<string, {
|
|
560
|
-
deployment: k8s.apps.v1.Deployment, // Kubernetes Deployment
|
|
561
|
-
service: k8s.core.v1.Service, // Kubernetes Service
|
|
562
|
-
ingress?: k8s.networking.v1.Ingress, // Ingress (if public)
|
|
563
|
-
internalUrl: pulumi.Output<string>, // Internal service URL
|
|
564
|
-
}>
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
### Environment Helpers (`@crossdelta/infrastructure/env`)
|
|
568
|
-
|
|
569
|
-
```typescript
|
|
570
|
-
import { getServicePort } from '@crossdelta/infrastructure/env'
|
|
571
|
-
|
|
572
|
-
// Read SERVICE_NAME_PORT from environment
|
|
573
|
-
const port = getServicePort('orders', 4001) // Fallback to 4001
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
## Cost Estimation (DOKS)
|
|
577
|
-
|
|
578
|
-
### Basic Setup
|
|
579
|
-
- **DOKS Cluster**: $12/month (control plane)
|
|
580
|
-
- **Worker Nodes**: 2× s-2vcpu-4gb = $36/month
|
|
581
|
-
- **Load Balancer**: $12/month (managed by DOKS)
|
|
582
|
-
- **Block Storage**: 10GB for NATS JetStream = $1/month
|
|
583
|
-
- **Total**: ~$61/month
|
|
584
|
-
|
|
585
|
-
### Production Setup
|
|
586
|
-
- **DOKS Cluster**: $12/month
|
|
587
|
-
- **Worker Nodes**: 3× s-2vcpu-4gb = $54/month (HA)
|
|
588
|
-
- **Load Balancer**: $12/month
|
|
589
|
-
- **Block Storage**: 50GB = $5/month
|
|
590
|
-
- **Total**: ~$83/month
|
|
591
|
-
|
|
592
|
-
### Scaling Considerations
|
|
593
|
-
|
|
594
|
-
| Workload | Node Size | Monthly Cost |
|
|
595
|
-
|----------|-----------|--------------|
|
|
596
|
-
| Development | 1× s-2vcpu-4gb | $12 + $18 = $30/mo |
|
|
597
|
-
| Staging | 2× s-2vcpu-4gb | $12 + $36 = $48/mo |
|
|
598
|
-
| Production | 3× s-4vcpu-8gb | $12 + $144 = $156/mo |
|
|
599
|
-
| High Traffic | 5× s-4vcpu-8gb | $12 + $240 = $252/mo |
|
|
600
|
-
|
|
601
|
-
**Benefits of Kubernetes:**
|
|
602
|
-
- **Auto-scaling**: HPA automatically adjusts pod count
|
|
603
|
-
- **Self-healing**: Failed pods restart automatically
|
|
604
|
-
- **Rolling updates**: Zero-downtime deployments
|
|
605
|
-
- **Resource efficiency**: Better node utilization vs. dedicated VMs
|
|
606
|
-
|
|
607
|
-
## Adding New Providers
|
|
608
|
-
|
|
609
|
-
The library is designed to be **provider-agnostic**. To add support for a new cloud provider (AWS EKS, Azure AKS, GKE):
|
|
610
|
-
|
|
611
|
-
### 1. Implement Provider-Specific Runtime Components
|
|
612
|
-
|
|
613
|
-
Create a new directory under `lib/runtimes/`:
|
|
614
|
-
|
|
615
|
-
```
|
|
616
|
-
lib/runtimes/
|
|
617
|
-
├── doks/ # DigitalOcean Kubernetes (reference implementation)
|
|
618
|
-
│ ├── nats.ts
|
|
619
|
-
│ ├── ingress.ts
|
|
620
|
-
│ └── cert-manager.ts
|
|
621
|
-
└── eks/ # Your new provider
|
|
622
|
-
├── nats.ts
|
|
623
|
-
├── ingress.ts
|
|
624
|
-
└── cert-manager.ts
|
|
625
|
-
```
|
|
626
|
-
|
|
627
|
-
### 2. Follow the Interface Pattern
|
|
628
|
-
|
|
629
|
-
Each component should export a deployment function:
|
|
630
|
-
|
|
631
|
-
```typescript
|
|
632
|
-
// lib/runtimes/eks/nats.ts
|
|
633
|
-
export function deployNats(
|
|
634
|
-
provider: k8s.Provider,
|
|
635
|
-
namespace: string,
|
|
636
|
-
config: NatsConfig
|
|
637
|
-
): NatsDeploymentResult {
|
|
638
|
-
// Provider-specific NATS deployment
|
|
639
|
-
// Use Helm, manifests, or provider-specific resources
|
|
640
|
-
|
|
641
|
-
return {
|
|
642
|
-
release,
|
|
643
|
-
internalUrl: `nats://nats.${namespace}.svc.cluster.local:4222`,
|
|
644
|
-
serviceDns: `nats.${namespace}.svc.cluster.local`,
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
```
|
|
648
|
-
|
|
649
|
-
### 3. Update deployRuntime()
|
|
650
|
-
|
|
651
|
-
Add your provider to `lib/core/runtime.ts`:
|
|
652
|
-
|
|
653
|
-
```typescript
|
|
654
|
-
// Deploy NATS if enabled
|
|
655
|
-
if (config.nats?.enabled) {
|
|
656
|
-
if (providerName === 'doks') {
|
|
657
|
-
const nats = deployNats(provider, namespace, config.nats.config || {})
|
|
658
|
-
result.natsUrl = pulumi.output(nats.internalUrl)
|
|
659
|
-
} else if (providerName === 'eks') {
|
|
660
|
-
const { deployNats } = require('../runtimes/eks/nats')
|
|
661
|
-
const nats = deployNats(provider, namespace, config.nats.config || {})
|
|
662
|
-
result.natsUrl = pulumi.output(nats.internalUrl)
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
```
|
|
666
|
-
|
|
667
|
-
### 4. Service Configs Remain Portable
|
|
668
|
-
|
|
669
|
-
Service configurations (`K8sServiceConfig`) work across all providers without changes. Only runtime components need provider-specific implementations.
|
|
124
|
+
| `deployRuntime(provider, ns, config)` | Deploy runtime components |
|
|
125
|
+
| `discoverServiceConfigs(dir, opts?)` | Auto-discover service configs from filesystem |
|
|
126
|
+
| `deployK8sServices(opts)` | Deploy services to cluster |
|
|
127
|
+
| `createNamespace(provider, name)` | Create k8s namespace |
|
|
128
|
+
| `createImagePullSecret(...)` | Create registry pull secret |
|
|
129
|
+
| `ports()` | Fluent port builder |
|
|
670
130
|
|
|
671
131
|
## License
|
|
672
132
|
|
package/dist/index.cjs
CHANGED
|
@@ -1177,6 +1177,26 @@ function createVPC(config) {
|
|
|
1177
1177
|
// lib/runtimes/doks/workloads.ts
|
|
1178
1178
|
var k8s6 = __toESM(require("@pulumi/kubernetes"));
|
|
1179
1179
|
var pulumi6 = __toESM(require("@pulumi/pulumi"));
|
|
1180
|
+
|
|
1181
|
+
// lib/runtimes/doks/probes.ts
|
|
1182
|
+
var buildProbe = (path, port, config) => path ? { ...config, httpGet: { path, port } } : { ...config, tcpSocket: { port } };
|
|
1183
|
+
var buildHealthProbes = (healthCheck, primaryPort, defaults) => {
|
|
1184
|
+
const probeConfig = {
|
|
1185
|
+
initialDelaySeconds: healthCheck.initialDelaySeconds ?? defaults.initialDelaySeconds,
|
|
1186
|
+
periodSeconds: healthCheck.periodSeconds ?? defaults.periodSeconds,
|
|
1187
|
+
failureThreshold: healthCheck.failureThreshold ?? defaults.failureThreshold,
|
|
1188
|
+
successThreshold: healthCheck.successThreshold ?? defaults.successThreshold,
|
|
1189
|
+
timeoutSeconds: healthCheck.timeoutSeconds ?? defaults.timeoutSeconds
|
|
1190
|
+
};
|
|
1191
|
+
const port = healthCheck.port ?? primaryPort;
|
|
1192
|
+
const readinessPath = healthCheck.readinessPath ?? healthCheck.httpPath;
|
|
1193
|
+
return {
|
|
1194
|
+
livenessProbe: buildProbe(healthCheck.httpPath, port, probeConfig),
|
|
1195
|
+
readinessProbe: buildProbe(readinessPath, port, probeConfig)
|
|
1196
|
+
};
|
|
1197
|
+
};
|
|
1198
|
+
|
|
1199
|
+
// lib/runtimes/doks/workloads.ts
|
|
1180
1200
|
var normalizeK8sConfig = (config) => {
|
|
1181
1201
|
if (config.ports) {
|
|
1182
1202
|
return config;
|
|
@@ -1295,24 +1315,11 @@ var createServiceVolumes = (provider, namespace, config, labels) => {
|
|
|
1295
1315
|
}));
|
|
1296
1316
|
return { pvcs, volumeMounts, volumes };
|
|
1297
1317
|
};
|
|
1298
|
-
var
|
|
1318
|
+
var buildHealthProbes2 = (config) => {
|
|
1299
1319
|
if (!config.healthCheck) {
|
|
1300
1320
|
return {};
|
|
1301
1321
|
}
|
|
1302
|
-
|
|
1303
|
-
const probeConfig = {
|
|
1304
|
-
initialDelaySeconds: healthCheck.initialDelaySeconds ?? DEFAULTS.healthCheck.initialDelaySeconds,
|
|
1305
|
-
periodSeconds: healthCheck.periodSeconds ?? DEFAULTS.healthCheck.periodSeconds,
|
|
1306
|
-
failureThreshold: healthCheck.failureThreshold ?? DEFAULTS.healthCheck.failureThreshold,
|
|
1307
|
-
successThreshold: healthCheck.successThreshold ?? DEFAULTS.healthCheck.successThreshold,
|
|
1308
|
-
timeoutSeconds: healthCheck.timeoutSeconds ?? DEFAULTS.healthCheck.timeoutSeconds
|
|
1309
|
-
};
|
|
1310
|
-
const port = healthCheck.port ?? config.ports.primary.port;
|
|
1311
|
-
const probe = healthCheck.httpPath ? { httpGet: { path: healthCheck.httpPath, port } } : { tcpSocket: { port } };
|
|
1312
|
-
return {
|
|
1313
|
-
livenessProbe: { ...probeConfig, ...probe },
|
|
1314
|
-
readinessProbe: { ...probeConfig, ...probe }
|
|
1315
|
-
};
|
|
1322
|
+
return buildHealthProbes(config.healthCheck, config.ports.primary.port, DEFAULTS.healthCheck);
|
|
1316
1323
|
};
|
|
1317
1324
|
var buildContainerPorts = (config) => {
|
|
1318
1325
|
const normalizeProtocol = (protocol) => protocol === "HTTP" || protocol === "HTTPS" || protocol === "GRPC" ? "TCP" : protocol ?? "TCP";
|
|
@@ -1411,7 +1418,7 @@ var deployK8sService = (provider, namespace, config) => {
|
|
|
1411
1418
|
const secret = createServiceSecret(provider, namespace, normalizedConfig, labels);
|
|
1412
1419
|
const envVars = buildEnvVars(normalizedConfig);
|
|
1413
1420
|
const { pvcs, volumeMounts, volumes } = createServiceVolumes(provider, namespace, normalizedConfig, labels);
|
|
1414
|
-
const { livenessProbe, readinessProbe } =
|
|
1421
|
+
const { livenessProbe, readinessProbe } = buildHealthProbes2(normalizedConfig);
|
|
1415
1422
|
const containerPorts = buildContainerPorts(normalizedConfig);
|
|
1416
1423
|
const servicePorts = buildServicePorts(normalizedConfig);
|
|
1417
1424
|
const deployment = new k8s6.apps.v1.Deployment(`${normalizedConfig.name}-deployment`, {
|
package/dist/index.js
CHANGED
|
@@ -1083,6 +1083,26 @@ function createVPC(config) {
|
|
|
1083
1083
|
// lib/runtimes/doks/workloads.ts
|
|
1084
1084
|
import * as k8s6 from "@pulumi/kubernetes";
|
|
1085
1085
|
import * as pulumi6 from "@pulumi/pulumi";
|
|
1086
|
+
|
|
1087
|
+
// lib/runtimes/doks/probes.ts
|
|
1088
|
+
var buildProbe = (path, port, config) => path ? { ...config, httpGet: { path, port } } : { ...config, tcpSocket: { port } };
|
|
1089
|
+
var buildHealthProbes = (healthCheck, primaryPort, defaults) => {
|
|
1090
|
+
const probeConfig = {
|
|
1091
|
+
initialDelaySeconds: healthCheck.initialDelaySeconds ?? defaults.initialDelaySeconds,
|
|
1092
|
+
periodSeconds: healthCheck.periodSeconds ?? defaults.periodSeconds,
|
|
1093
|
+
failureThreshold: healthCheck.failureThreshold ?? defaults.failureThreshold,
|
|
1094
|
+
successThreshold: healthCheck.successThreshold ?? defaults.successThreshold,
|
|
1095
|
+
timeoutSeconds: healthCheck.timeoutSeconds ?? defaults.timeoutSeconds
|
|
1096
|
+
};
|
|
1097
|
+
const port = healthCheck.port ?? primaryPort;
|
|
1098
|
+
const readinessPath = healthCheck.readinessPath ?? healthCheck.httpPath;
|
|
1099
|
+
return {
|
|
1100
|
+
livenessProbe: buildProbe(healthCheck.httpPath, port, probeConfig),
|
|
1101
|
+
readinessProbe: buildProbe(readinessPath, port, probeConfig)
|
|
1102
|
+
};
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1105
|
+
// lib/runtimes/doks/workloads.ts
|
|
1086
1106
|
var normalizeK8sConfig = (config) => {
|
|
1087
1107
|
if (config.ports) {
|
|
1088
1108
|
return config;
|
|
@@ -1201,24 +1221,11 @@ var createServiceVolumes = (provider, namespace, config, labels) => {
|
|
|
1201
1221
|
}));
|
|
1202
1222
|
return { pvcs, volumeMounts, volumes };
|
|
1203
1223
|
};
|
|
1204
|
-
var
|
|
1224
|
+
var buildHealthProbes2 = (config) => {
|
|
1205
1225
|
if (!config.healthCheck) {
|
|
1206
1226
|
return {};
|
|
1207
1227
|
}
|
|
1208
|
-
|
|
1209
|
-
const probeConfig = {
|
|
1210
|
-
initialDelaySeconds: healthCheck.initialDelaySeconds ?? DEFAULTS.healthCheck.initialDelaySeconds,
|
|
1211
|
-
periodSeconds: healthCheck.periodSeconds ?? DEFAULTS.healthCheck.periodSeconds,
|
|
1212
|
-
failureThreshold: healthCheck.failureThreshold ?? DEFAULTS.healthCheck.failureThreshold,
|
|
1213
|
-
successThreshold: healthCheck.successThreshold ?? DEFAULTS.healthCheck.successThreshold,
|
|
1214
|
-
timeoutSeconds: healthCheck.timeoutSeconds ?? DEFAULTS.healthCheck.timeoutSeconds
|
|
1215
|
-
};
|
|
1216
|
-
const port = healthCheck.port ?? config.ports.primary.port;
|
|
1217
|
-
const probe = healthCheck.httpPath ? { httpGet: { path: healthCheck.httpPath, port } } : { tcpSocket: { port } };
|
|
1218
|
-
return {
|
|
1219
|
-
livenessProbe: { ...probeConfig, ...probe },
|
|
1220
|
-
readinessProbe: { ...probeConfig, ...probe }
|
|
1221
|
-
};
|
|
1228
|
+
return buildHealthProbes(config.healthCheck, config.ports.primary.port, DEFAULTS.healthCheck);
|
|
1222
1229
|
};
|
|
1223
1230
|
var buildContainerPorts = (config) => {
|
|
1224
1231
|
const normalizeProtocol = (protocol) => protocol === "HTTP" || protocol === "HTTPS" || protocol === "GRPC" ? "TCP" : protocol ?? "TCP";
|
|
@@ -1317,7 +1324,7 @@ var deployK8sService = (provider, namespace, config) => {
|
|
|
1317
1324
|
const secret = createServiceSecret(provider, namespace, normalizedConfig, labels);
|
|
1318
1325
|
const envVars = buildEnvVars(normalizedConfig);
|
|
1319
1326
|
const { pvcs, volumeMounts, volumes } = createServiceVolumes(provider, namespace, normalizedConfig, labels);
|
|
1320
|
-
const { livenessProbe, readinessProbe } =
|
|
1327
|
+
const { livenessProbe, readinessProbe } = buildHealthProbes2(normalizedConfig);
|
|
1321
1328
|
const containerPorts = buildContainerPorts(normalizedConfig);
|
|
1322
1329
|
const servicePorts = buildServicePorts(normalizedConfig);
|
|
1323
1330
|
const deployment = new k8s6.apps.v1.Deployment(`${normalizedConfig.name}-deployment`, {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { K8sHealthCheck } from './types';
|
|
2
|
+
export interface ProbeHttp {
|
|
3
|
+
httpGet: {
|
|
4
|
+
path: string;
|
|
5
|
+
port: number;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export interface ProbeTcp {
|
|
9
|
+
tcpSocket: {
|
|
10
|
+
port: number;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface ProbeConfig {
|
|
14
|
+
initialDelaySeconds: number;
|
|
15
|
+
periodSeconds: number;
|
|
16
|
+
failureThreshold: number;
|
|
17
|
+
successThreshold: number;
|
|
18
|
+
timeoutSeconds: number;
|
|
19
|
+
}
|
|
20
|
+
export type Probe = ProbeConfig & (ProbeHttp | ProbeTcp);
|
|
21
|
+
export interface HealthProbes {
|
|
22
|
+
livenessProbe?: Probe;
|
|
23
|
+
readinessProbe?: Probe;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Builds liveness and readiness probes from a service health check config.
|
|
27
|
+
*
|
|
28
|
+
* - `httpPath` → liveness probe
|
|
29
|
+
* - `readinessPath` → readiness probe (falls back to `httpPath` when omitted)
|
|
30
|
+
* - Neither → tcpSocket probe on the primary port
|
|
31
|
+
*/
|
|
32
|
+
export declare const buildHealthProbes: (healthCheck: K8sHealthCheck, primaryPort: number, defaults: ProbeConfig) => HealthProbes;
|
|
@@ -96,8 +96,10 @@ export interface K8sResourceConfig {
|
|
|
96
96
|
* Health check configuration for Kubernetes probes.
|
|
97
97
|
*/
|
|
98
98
|
export interface K8sHealthCheck {
|
|
99
|
-
/** HTTP path for
|
|
99
|
+
/** HTTP path for liveness probe (e.g., '/health') */
|
|
100
100
|
httpPath?: string;
|
|
101
|
+
/** HTTP path for readiness probe — if omitted, falls back to httpPath (e.g., '/health/ready') */
|
|
102
|
+
readinessPath?: string;
|
|
101
103
|
/** Port for health check (defaults to containerPort) */
|
|
102
104
|
port?: number;
|
|
103
105
|
/** Initial delay before starting probes in seconds (default: 10) */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crossdelta/infrastructure",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@crossdelta/cloudevents": "0.6.
|
|
38
|
+
"@crossdelta/cloudevents": "0.6.7"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@pulumi/digitalocean": "^4.0.0",
|