@crossdelta/infrastructure 0.9.0 → 0.10.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.
package/README.md CHANGED
@@ -3,17 +3,58 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@crossdelta/infrastructure.svg)](https://www.npmjs.com/package/@crossdelta/infrastructure)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- Opinionated Pulumi abstractions for deploying microservices to Kubernetes. Turns per-service config objects into Deployments, Services, Ingress, probes, and secrets.
6
+ Pulumi abstractions that turn per-service config objects into complete Kubernetes deployments. You describe **what** each service needs (ports, env, secrets, health checks), the package handles **how** (Deployments, Services, Ingress, TLS, probes, pull secrets).
7
7
 
8
- ## Installation
8
+ **What you skip writing:**
9
+ - Boilerplate K8s YAML / Pulumi resource declarations per service
10
+ - NATS JetStream + cert-manager + NGINX Ingress setup
11
+ - Docker registry secrets, health probes, rolling update strategies
12
+ - Multi-environment concerns (shared cluster, per-stack namespaces)
13
+
14
+ ## Install
9
15
 
10
16
  ```bash
11
17
  npm install @crossdelta/infrastructure @pulumi/pulumi @pulumi/kubernetes
12
18
  ```
13
19
 
14
- ## Usage
20
+ ## End-to-End Example
15
21
 
16
- ### Service config
22
+ One `index.ts` that provisions a cluster, runtime, and all services:
23
+
24
+ ```typescript
25
+ import {
26
+ createDOKSCluster,
27
+ createVPC,
28
+ createNamespace,
29
+ deployRuntime,
30
+ deployK8sServices,
31
+ discoverServiceConfigs,
32
+ } from '@crossdelta/infrastructure'
33
+
34
+ // 1. Cluster
35
+ const vpc = createVPC({ name: 'my-vpc', region: 'fra1' })
36
+ const { provider } = createDOKSCluster({
37
+ name: 'my-cluster',
38
+ vpcUuid: vpc.id,
39
+ nodePool: { name: 'default', size: 's-4vcpu-8gb', nodeCount: 1 },
40
+ })
41
+ createNamespace(provider, 'my-namespace')
42
+
43
+ // 2. Runtime (toggle what you need)
44
+ const runtime = deployRuntime(provider, 'my-namespace', {
45
+ nats: { enabled: true, config: { replicas: 1, jetstream: { enabled: true } } },
46
+ ingress: { enabled: true },
47
+ certManager: { enabled: true, config: { email: 'ops@example.com' } },
48
+ })
49
+
50
+ // 3. Services (auto-discovered from infra/services/*.ts)
51
+ const configs = discoverServiceConfigs('services')
52
+ deployK8sServices(provider, 'my-namespace', configs)
53
+ ```
54
+
55
+ ## Service Config
56
+
57
+ Each file in `infra/services/` exports one config. The package derives Deployment, Service, Ingress, Secret, and probes from it:
17
58
 
18
59
  ```typescript
19
60
  import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
@@ -21,14 +62,9 @@ import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
21
62
  const config: K8sServiceConfig = {
22
63
  name: 'my-service',
23
64
  ports: ports().http(4000).public().build(),
24
- replicas: 1,
25
- healthCheck: {
26
- httpPath: '/health',
27
- readinessPath: '/health/ready',
28
- },
65
+ healthCheck: { httpPath: '/health' },
29
66
  ingress: { path: '/api', host: 'example.com' },
30
67
  env: { DATABASE_URL: dbUrl },
31
- containerEnv: { NODE_OPTIONS: '--max-old-space-size=384' },
32
68
  secrets: { API_KEY: apiKey },
33
69
  resources: {
34
70
  requests: { cpu: '50m', memory: '64Mi' },
@@ -39,104 +75,30 @@ const config: K8sServiceConfig = {
39
75
  export default config
40
76
  ```
41
77
 
42
- ### Deploy services
78
+ See `K8sServiceConfig` type for all available fields (replicas, volumes, strategy, containerEnv, etc.).
43
79
 
44
- ```typescript
45
- import { deployK8sServices, discoverServiceConfigs } from '@crossdelta/infrastructure'
46
-
47
- const serviceConfigs = discoverServiceConfigs('services')
80
+ ## Shared Cluster (Multi-Stack)
48
81
 
49
- deployK8sServices({
50
- provider,
51
- namespace,
52
- serviceConfigs,
53
- env: { NATS_URL: natsUrl },
54
- })
55
- ```
56
-
57
- ### Deploy runtime components
82
+ When multiple Pulumi stacks share one cluster, use `clusterName`/`vpcName` to pin the DigitalOcean resource name (default appends the stack name):
58
83
 
59
84
  ```typescript
60
- import { deployRuntime } from '@crossdelta/infrastructure'
61
-
62
- const runtime = deployRuntime(provider, namespace, {
63
- nats: { enabled: true, config: { replicas: 1, jetstream: { enabled: true } } },
64
- ingress: {
65
- enabled: true,
66
- config: {
67
- nginxConfig: {
68
- 'proxy-buffer-size': '16k', // Increase for large auth cookies/JWTs
69
- },
70
- },
71
- },
72
- certManager: { enabled: true, config: { email: 'ops@example.com' } },
85
+ // Stack A (stage) owns the cluster
86
+ const { provider, kubeconfig } = createDOKSCluster({
87
+ name: 'my-cluster',
88
+ clusterName: 'my-cluster',
89
+ vpcUuid: vpc.id,
90
+ nodePool: { name: 'default', size: 's-4vcpu-8gb', nodeCount: 1 },
73
91
  })
92
+ export const clusterKubeconfig = kubeconfig
93
+
94
+ // Stack B (production) — references the shared cluster
95
+ const stageStack = new StackReference('org/project/stage')
96
+ const provider = createK8sProviderFromKubeconfig(
97
+ 'production',
98
+ stageStack.getOutput('clusterKubeconfig'),
99
+ )
74
100
  ```
75
101
 
76
- ## K8sServiceConfig
77
-
78
- ```typescript
79
- interface K8sServiceConfig {
80
- name: string
81
- ports: PortConfig
82
- replicas?: number
83
- resources?: {
84
- requests?: { cpu: string; memory: string }
85
- limits?: { cpu: string; memory: string }
86
- }
87
- healthCheck?: {
88
- httpPath: string
89
- readinessPath?: string // Falls back to httpPath
90
- initialDelaySeconds?: number // Default: 10
91
- periodSeconds?: number // Default: 10
92
- failureThreshold?: number // Default: 3
93
- }
94
- ingress?: {
95
- path: string
96
- host?: string
97
- }
98
- env?: Record<string, pulumi.Input<string>>
99
- containerEnv?: Record<string, pulumi.Input<string>>
100
- secrets?: Record<string, pulumi.Input<string>>
101
- image?: string
102
- skip?: boolean
103
- }
104
- ```
105
-
106
- ## Fluent Ports API
107
-
108
- ```typescript
109
- import { ports } from '@crossdelta/infrastructure'
110
-
111
- ports().http(4000).build() // Simple HTTP
112
- ports().http(4000).public().build() // Public (ingress)
113
- ports().grpc(50051).build() // gRPC
114
- ports().http(4000).addHttp(8080, 'metrics').build() // Multiple ports
115
- ```
116
-
117
- | Method | Description |
118
- |--------|-------------|
119
- | `.http(port)` | HTTP primary port |
120
- | `.grpc(port)` | gRPC primary port |
121
- | `.primary(port, name?)` | Custom primary port |
122
- | `.add(port, name?, protocol?)` | Additional port |
123
- | `.addHttp(port, name?)` | Additional HTTP port |
124
- | `.addGrpc(port, name?)` | Additional gRPC port |
125
- | `.public()` | Expose via ingress |
126
- | `.protocol(type)` | Set protocol (TCP, UDP, SCTP) |
127
- | `.build()` | Build config |
128
-
129
- ## API Reference
130
-
131
- | Function | Description |
132
- |----------|-------------|
133
- | `deployRuntime(provider, ns, config)` | Deploy runtime components |
134
- | `discoverServiceConfigs(dir, opts?)` | Auto-discover service configs from filesystem |
135
- | `deployK8sServices(opts)` | Deploy services to cluster |
136
- | `createNamespace(provider, name)` | Create k8s namespace |
137
- | `createImagePullSecret(...)` | Create registry pull secret |
138
- | `ports()` | Fluent port builder |
139
-
140
102
  ## License
141
103
 
142
104
  MIT © [crossdelta](https://crossdelta.de)
package/dist/index.cjs CHANGED
@@ -1139,7 +1139,7 @@ function createDOKSCluster(config) {
1139
1139
  const stack = pulumi4.getStack();
1140
1140
  const region = config.region ?? "fra1";
1141
1141
  const cluster = new digitalocean.KubernetesCluster(config.name, {
1142
- name: `${config.name}-${stack}`,
1142
+ name: config.clusterName ?? `${config.name}-${stack}`,
1143
1143
  region,
1144
1144
  version: config.version ?? "1.32.10-do.0",
1145
1145
  vpcUuid: config.vpcUuid,
@@ -1190,7 +1190,7 @@ function createVPC(config) {
1190
1190
  const stack = pulumi5.getStack();
1191
1191
  const region = config.region ?? "fra1";
1192
1192
  return new digitalocean2.Vpc(config.name, {
1193
- name: `${config.name}-${stack}`,
1193
+ name: config.vpcName ?? `${config.name}-${stack}`,
1194
1194
  region,
1195
1195
  description: config.description ?? `VPC for ${config.name}`,
1196
1196
  ipRange: config.ipRange
package/dist/index.js CHANGED
@@ -1045,7 +1045,7 @@ function createDOKSCluster(config) {
1045
1045
  const stack = pulumi4.getStack();
1046
1046
  const region = config.region ?? "fra1";
1047
1047
  const cluster = new digitalocean.KubernetesCluster(config.name, {
1048
- name: `${config.name}-${stack}`,
1048
+ name: config.clusterName ?? `${config.name}-${stack}`,
1049
1049
  region,
1050
1050
  version: config.version ?? "1.32.10-do.0",
1051
1051
  vpcUuid: config.vpcUuid,
@@ -1096,7 +1096,7 @@ function createVPC(config) {
1096
1096
  const stack = pulumi5.getStack();
1097
1097
  const region = config.region ?? "fra1";
1098
1098
  return new digitalocean2.Vpc(config.name, {
1099
- name: `${config.name}-${stack}`,
1099
+ name: config.vpcName ?? `${config.name}-${stack}`,
1100
1100
  region,
1101
1101
  description: config.description ?? `VPC for ${config.name}`,
1102
1102
  ipRange: config.ipRange
@@ -28,8 +28,10 @@ export type Region = digitalocean.Region;
28
28
  * ```
29
29
  */
30
30
  export interface DOKSClusterConfig {
31
- /** Cluster name (will be suffixed with stack name) */
31
+ /** Pulumi resource name (logical, for state tracking) */
32
32
  name: string;
33
+ /** Actual cluster name in DigitalOcean. Defaults to `${name}-${stack}` */
34
+ clusterName?: string;
33
35
  /** DigitalOcean region (defaults to 'fra1') */
34
36
  region?: digitalocean.Region | string;
35
37
  /** Kubernetes version - use `doctl kubernetes options versions` to list available */
@@ -65,8 +67,10 @@ export interface DOKSClusterConfig {
65
67
  * Simplified VPC configuration with stack-aware naming.
66
68
  */
67
69
  export interface VPCConfig {
68
- /** VPC name (will be suffixed with stack name) */
70
+ /** Pulumi resource name (logical, for state tracking) */
69
71
  name: string;
72
+ /** Actual VPC name in DigitalOcean. Defaults to `${name}-${stack}` */
73
+ vpcName?: string;
70
74
  /** DigitalOcean region (defaults to 'fra1') */
71
75
  region?: digitalocean.Region | string;
72
76
  /** Optional description */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/infrastructure",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "publishConfig": {