@crossdelta/infrastructure 0.2.17 → 0.2.19
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/helpers/droplet-builder.d.ts +6 -0
- package/dist/index.cjs +26222 -67
- package/dist/index.d.ts +1 -0
- package/dist/index.js +26222 -67
- package/dist/runtimes/doks/cert-manager.d.ts +50 -0
- package/dist/runtimes/doks/cluster.d.ts +61 -0
- package/dist/runtimes/doks/index.d.ts +54 -0
- package/dist/runtimes/doks/ingress.d.ts +57 -0
- package/dist/runtimes/doks/nats.d.ts +51 -0
- package/dist/runtimes/doks/types.d.ts +313 -0
- package/dist/runtimes/doks/vpc.d.ts +18 -0
- package/dist/runtimes/doks/workloads.d.ts +79 -0
- package/dist/types/index.d.ts +4 -0
- package/package.json +4 -2
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cert-Manager deployment for DOKS
|
|
3
|
+
*
|
|
4
|
+
* Deploys cert-manager via Helm chart for automatic TLS certificate management
|
|
5
|
+
* with Let's Encrypt.
|
|
6
|
+
*/
|
|
7
|
+
import * as k8s from '@pulumi/kubernetes';
|
|
8
|
+
export interface CertManagerConfig {
|
|
9
|
+
/** Email for Let's Encrypt notifications */
|
|
10
|
+
email: string;
|
|
11
|
+
/** Use Let's Encrypt staging (for testing) or production */
|
|
12
|
+
staging?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface CertManagerResult {
|
|
15
|
+
/** The Helm release */
|
|
16
|
+
release: k8s.helm.v3.Release;
|
|
17
|
+
/** The namespace where cert-manager is deployed */
|
|
18
|
+
namespace: string;
|
|
19
|
+
/** The ClusterIssuer for Let's Encrypt */
|
|
20
|
+
clusterIssuer: k8s.apiextensions.CustomResource;
|
|
21
|
+
/** ClusterIssuer name to use in Ingress annotations */
|
|
22
|
+
issuerName: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Deploy cert-manager to the cluster with Let's Encrypt ClusterIssuer.
|
|
26
|
+
*
|
|
27
|
+
* This creates:
|
|
28
|
+
* - cert-manager deployment with CRDs
|
|
29
|
+
* - ClusterIssuer for Let's Encrypt (production or staging)
|
|
30
|
+
*
|
|
31
|
+
* After deployment, add these annotations to your Ingress:
|
|
32
|
+
* ```yaml
|
|
33
|
+
* annotations:
|
|
34
|
+
* cert-manager.io/cluster-issuer: "letsencrypt-production"
|
|
35
|
+
* spec:
|
|
36
|
+
* tls:
|
|
37
|
+
* - hosts:
|
|
38
|
+
* - example.com
|
|
39
|
+
* secretName: example-com-tls
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const certManager = deployCertManager(provider, {
|
|
45
|
+
* email: 'admin@example.com',
|
|
46
|
+
* staging: false, // Use production Let's Encrypt
|
|
47
|
+
* })
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function deployCertManager(provider: k8s.Provider, config: CertManagerConfig): CertManagerResult;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as digitalocean from '@pulumi/digitalocean';
|
|
2
|
+
import * as k8s from '@pulumi/kubernetes';
|
|
3
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
4
|
+
import type { DOKSClusterConfig } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Result of creating a DOKS cluster.
|
|
7
|
+
*/
|
|
8
|
+
export interface DOKSClusterResult {
|
|
9
|
+
/** The DigitalOcean Kubernetes cluster resource */
|
|
10
|
+
cluster: digitalocean.KubernetesCluster;
|
|
11
|
+
/** Kubernetes provider configured for this cluster */
|
|
12
|
+
provider: k8s.Provider;
|
|
13
|
+
/** Kubeconfig for the cluster (raw YAML) */
|
|
14
|
+
kubeconfig: pulumi.Output<string>;
|
|
15
|
+
/** Cluster API endpoint URL */
|
|
16
|
+
endpoint: pulumi.Output<string>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates a DigitalOcean Kubernetes (DOKS) cluster with a configured K8s provider.
|
|
20
|
+
*
|
|
21
|
+
* This function:
|
|
22
|
+
* - Creates a DOKS cluster with the specified configuration
|
|
23
|
+
* - Automatically creates a @pulumi/kubernetes Provider connected to the cluster
|
|
24
|
+
* - Uses sensible defaults for maintenance, upgrades, and tagging
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const { cluster, provider, kubeconfig } = createDOKSCluster({
|
|
29
|
+
* name: 'orderboss-cluster',
|
|
30
|
+
* region: 'fra1',
|
|
31
|
+
* vpcUuid: vpc.id,
|
|
32
|
+
* nodePool: {
|
|
33
|
+
* name: 'default',
|
|
34
|
+
* size: 's-2vcpu-4gb',
|
|
35
|
+
* nodeCount: 3,
|
|
36
|
+
* },
|
|
37
|
+
* })
|
|
38
|
+
*
|
|
39
|
+
* // Use the provider for K8s resources
|
|
40
|
+
* const namespace = new k8s.core.v1.Namespace('app', { ... }, { provider })
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function createDOKSCluster(config: DOKSClusterConfig): DOKSClusterResult;
|
|
44
|
+
/**
|
|
45
|
+
* Creates a Kubernetes provider from an existing kubeconfig string.
|
|
46
|
+
*
|
|
47
|
+
* Useful for:
|
|
48
|
+
* - Connecting to existing clusters
|
|
49
|
+
* - Using kubeconfig from CI/CD secrets
|
|
50
|
+
* - Testing against local/remote clusters
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* // From a secret
|
|
55
|
+
* const provider = createK8sProviderFromKubeconfig('my-cluster', kubeconfigSecret)
|
|
56
|
+
*
|
|
57
|
+
* // From environment
|
|
58
|
+
* const provider = createK8sProviderFromKubeconfig('local', process.env.KUBECONFIG_CONTENT!)
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare function createK8sProviderFromKubeconfig(name: string, kubeconfig: pulumi.Input<string>): k8s.Provider;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DigitalOcean Kubernetes (DOKS) Runtime Module
|
|
3
|
+
*
|
|
4
|
+
* This module provides helpers for deploying workloads to DigitalOcean Kubernetes clusters.
|
|
5
|
+
*
|
|
6
|
+
* ## Features
|
|
7
|
+
*
|
|
8
|
+
* - VPC creation for private networking
|
|
9
|
+
* - DOKS cluster provisioning with auto-configured K8s provider
|
|
10
|
+
* - NATS + JetStream deployment via Helm
|
|
11
|
+
* - Generic K8s workload deployment (Deployment, Service, Ingress, Secrets, PVCs)
|
|
12
|
+
*
|
|
13
|
+
* ## Usage
|
|
14
|
+
*
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { createVPC, createDOKSCluster, deployNats, deployK8sService, createNamespace } from '@crossdelta/infrastructure'
|
|
17
|
+
*
|
|
18
|
+
* // 1. Create VPC
|
|
19
|
+
* const vpc = createVPC({ name: 'my-vpc', region: 'fra1' })
|
|
20
|
+
*
|
|
21
|
+
* // 2. Create DOKS cluster
|
|
22
|
+
* const { provider } = createDOKSCluster({
|
|
23
|
+
* name: 'my-cluster',
|
|
24
|
+
* vpcUuid: vpc.id,
|
|
25
|
+
* nodePool: { name: 'default', size: 's-2vcpu-4gb', nodeCount: 3 },
|
|
26
|
+
* })
|
|
27
|
+
*
|
|
28
|
+
* // 3. Create namespace
|
|
29
|
+
* const namespace = createNamespace(provider, 'my-app')
|
|
30
|
+
*
|
|
31
|
+
* // 4. Deploy NATS
|
|
32
|
+
* const nats = deployNats(provider, 'my-app', { jetstream: { enabled: true } })
|
|
33
|
+
*
|
|
34
|
+
* // 5. Deploy services
|
|
35
|
+
* deployK8sService(provider, 'my-app', {
|
|
36
|
+
* name: 'api',
|
|
37
|
+
* image: 'my-registry/api:latest',
|
|
38
|
+
* containerPort: 3000,
|
|
39
|
+
* ingress: { path: '/api' },
|
|
40
|
+
* })
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @module
|
|
44
|
+
*/
|
|
45
|
+
export type { CertManagerConfig, CertManagerResult } from './cert-manager';
|
|
46
|
+
export { deployCertManager } from './cert-manager';
|
|
47
|
+
export type { DOKSClusterResult } from './cluster';
|
|
48
|
+
export { createDOKSCluster, createK8sProviderFromKubeconfig } from './cluster';
|
|
49
|
+
export type { NginxIngressConfig, NginxIngressResult } from './ingress';
|
|
50
|
+
export { deployNginxIngress } from './ingress';
|
|
51
|
+
export { buildNatsUrl, deployNats } from './nats';
|
|
52
|
+
export type { DeployK8sServicesOptions, DOKSClusterArgs, DOKSClusterConfig, K8sHealthCheck, K8sIngressConfig, K8sResourceConfig, K8sResources, K8sServiceConfig, K8sServiceDeploymentResult, K8sVolumeMount, NatsConfig, NatsDeploymentResult, Region, VPCConfig, VpcArgs, } from './types';
|
|
53
|
+
export { createVPC } from './vpc';
|
|
54
|
+
export { buildInternalUrl, createImagePullSecret, createNamespace, deployK8sService, deployK8sServices, } from './workloads';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ingress Controller deployment for DOKS
|
|
3
|
+
*
|
|
4
|
+
* Deploys nginx-ingress-controller via Helm chart.
|
|
5
|
+
* Creates a single LoadBalancer that routes traffic to all services.
|
|
6
|
+
*/
|
|
7
|
+
import * as k8s from '@pulumi/kubernetes';
|
|
8
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
9
|
+
export interface NginxIngressConfig {
|
|
10
|
+
/** Number of replicas for the ingress controller (default: 1) */
|
|
11
|
+
replicas?: number;
|
|
12
|
+
/** Resource requests/limits */
|
|
13
|
+
resources?: {
|
|
14
|
+
requests?: {
|
|
15
|
+
cpu?: string;
|
|
16
|
+
memory?: string;
|
|
17
|
+
};
|
|
18
|
+
limits?: {
|
|
19
|
+
cpu?: string;
|
|
20
|
+
memory?: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/** Enable proxy protocol for preserving client IPs (default: true for DO) */
|
|
24
|
+
useProxyProtocol?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface NginxIngressResult {
|
|
27
|
+
/** The Helm release */
|
|
28
|
+
release: k8s.helm.v3.Release;
|
|
29
|
+
/** The LoadBalancer service name */
|
|
30
|
+
serviceName: string;
|
|
31
|
+
/** The namespace where ingress controller is deployed */
|
|
32
|
+
namespace: string;
|
|
33
|
+
/** The LoadBalancer external IP (available after deployment) */
|
|
34
|
+
loadBalancerIp: pulumi.Output<string>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Deploy nginx-ingress-controller to the cluster.
|
|
38
|
+
*
|
|
39
|
+
* This creates:
|
|
40
|
+
* - Ingress controller deployment
|
|
41
|
+
* - LoadBalancer service (~$12/month on DigitalOcean)
|
|
42
|
+
* - Required RBAC resources
|
|
43
|
+
*
|
|
44
|
+
* After deployment, Ingress resources will automatically get an external IP.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const ingress = deployNginxIngress(provider, {
|
|
49
|
+
* replicas: 1,
|
|
50
|
+
* resources: {
|
|
51
|
+
* requests: { cpu: '100m', memory: '128Mi' },
|
|
52
|
+
* limits: { cpu: '200m', memory: '256Mi' },
|
|
53
|
+
* },
|
|
54
|
+
* })
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function deployNginxIngress(provider: k8s.Provider, config?: NginxIngressConfig): NginxIngressResult;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as k8s from '@pulumi/kubernetes';
|
|
2
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
3
|
+
import type { NatsConfig, NatsDeploymentResult } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Deploys NATS with JetStream to a Kubernetes cluster using the official Helm chart.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - NATS cluster with configurable replicas (default: 3 for HA)
|
|
9
|
+
* - JetStream enabled with persistent storage via PVC
|
|
10
|
+
* - Optional authentication
|
|
11
|
+
* - DigitalOcean block storage integration
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const nats = deployNats(provider, 'orderboss', {
|
|
16
|
+
* replicas: 3,
|
|
17
|
+
* jetstream: {
|
|
18
|
+
* enabled: true,
|
|
19
|
+
* storageSize: '20Gi',
|
|
20
|
+
* },
|
|
21
|
+
* auth: {
|
|
22
|
+
* enabled: true,
|
|
23
|
+
* user: natsUser,
|
|
24
|
+
* password: natsPassword,
|
|
25
|
+
* },
|
|
26
|
+
* })
|
|
27
|
+
*
|
|
28
|
+
* // Connect from other services:
|
|
29
|
+
* // nats://nats.orderboss.svc.cluster.local:4222
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function deployNats(provider: k8s.Provider, namespace: string, config?: NatsConfig): NatsDeploymentResult;
|
|
33
|
+
/**
|
|
34
|
+
* Creates a NATS connection URL with optional authentication.
|
|
35
|
+
*
|
|
36
|
+
* Note: Username and password are URL-encoded to handle special characters.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Without auth
|
|
41
|
+
* const url = buildNatsUrl('orderboss') // nats://nats.orderboss.svc.cluster.local:4222
|
|
42
|
+
*
|
|
43
|
+
* // With auth
|
|
44
|
+
* const url = buildNatsUrl('orderboss', { user: 'myuser', password: 'secret' })
|
|
45
|
+
* // nats://myuser:secret@nats.orderboss.svc.cluster.local:4222
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildNatsUrl(namespace: string, auth?: {
|
|
49
|
+
user: pulumi.Input<string>;
|
|
50
|
+
password: pulumi.Input<string>;
|
|
51
|
+
}): pulumi.Output<string>;
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import type * as digitalocean from '@pulumi/digitalocean';
|
|
2
|
+
import type * as pulumi from '@pulumi/pulumi';
|
|
3
|
+
/** DigitalOcean Kubernetes Cluster Args (from @pulumi/digitalocean) */
|
|
4
|
+
export type DOKSClusterArgs = digitalocean.KubernetesClusterArgs;
|
|
5
|
+
/** DigitalOcean VPC Args (from @pulumi/digitalocean) */
|
|
6
|
+
export type VpcArgs = digitalocean.VpcArgs;
|
|
7
|
+
/** DigitalOcean Region enum */
|
|
8
|
+
export type Region = digitalocean.Region;
|
|
9
|
+
/**
|
|
10
|
+
* Simplified DOKS cluster configuration.
|
|
11
|
+
*
|
|
12
|
+
* Wraps `digitalocean.KubernetesClusterArgs` with sensible defaults
|
|
13
|
+
* and a more ergonomic interface for common use cases.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const cluster = createDOKSCluster({
|
|
18
|
+
* name: 'orderboss-cluster',
|
|
19
|
+
* region: 'fra1',
|
|
20
|
+
* vpcUuid: vpc.id,
|
|
21
|
+
* nodePool: {
|
|
22
|
+
* name: 'default',
|
|
23
|
+
* size: 's-2vcpu-4gb',
|
|
24
|
+
* nodeCount: 3,
|
|
25
|
+
* },
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export interface DOKSClusterConfig {
|
|
30
|
+
/** Cluster name (will be suffixed with stack name) */
|
|
31
|
+
name: string;
|
|
32
|
+
/** DigitalOcean region (defaults to 'fra1') */
|
|
33
|
+
region?: digitalocean.Region | string;
|
|
34
|
+
/** Kubernetes version - use `doctl kubernetes options versions` to list available */
|
|
35
|
+
version?: string;
|
|
36
|
+
/** Node pool configuration */
|
|
37
|
+
nodePool: {
|
|
38
|
+
/** Node pool name */
|
|
39
|
+
name: string;
|
|
40
|
+
/** Droplet size slug (e.g., 's-2vcpu-4gb') */
|
|
41
|
+
size: string;
|
|
42
|
+
/** Number of nodes (ignored if autoScale is set) */
|
|
43
|
+
nodeCount?: number;
|
|
44
|
+
/** Auto-scale configuration */
|
|
45
|
+
autoScale?: {
|
|
46
|
+
minNodes: number;
|
|
47
|
+
maxNodes: number;
|
|
48
|
+
};
|
|
49
|
+
/** Labels for nodes */
|
|
50
|
+
labels?: Record<string, string>;
|
|
51
|
+
};
|
|
52
|
+
/** VPC UUID for private networking */
|
|
53
|
+
vpcUuid?: pulumi.Input<string>;
|
|
54
|
+
/** Enable HA control plane (defaults to false) */
|
|
55
|
+
ha?: boolean;
|
|
56
|
+
/** Enable auto-upgrade (defaults to true) */
|
|
57
|
+
autoUpgrade?: boolean;
|
|
58
|
+
/** Enable surge upgrades (defaults to true) */
|
|
59
|
+
surgeUpgrade?: boolean;
|
|
60
|
+
/** Tags for the cluster */
|
|
61
|
+
tags?: string[];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Simplified VPC configuration with stack-aware naming.
|
|
65
|
+
*/
|
|
66
|
+
export interface VPCConfig {
|
|
67
|
+
/** VPC name (will be suffixed with stack name) */
|
|
68
|
+
name: string;
|
|
69
|
+
/** DigitalOcean region (defaults to 'fra1') */
|
|
70
|
+
region?: digitalocean.Region | string;
|
|
71
|
+
/** Optional description */
|
|
72
|
+
description?: string;
|
|
73
|
+
/** IP range for the VPC (e.g., '10.10.10.0/24') */
|
|
74
|
+
ipRange?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resource limits and requests for Kubernetes containers.
|
|
78
|
+
*/
|
|
79
|
+
export interface K8sResources {
|
|
80
|
+
/** CPU (e.g., '100m', '0.5', '2') */
|
|
81
|
+
cpu?: string;
|
|
82
|
+
/** Memory (e.g., '128Mi', '1Gi') */
|
|
83
|
+
memory?: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Resource configuration for Kubernetes pods.
|
|
87
|
+
*/
|
|
88
|
+
export interface K8sResourceConfig {
|
|
89
|
+
/** Resource requests (guaranteed resources) */
|
|
90
|
+
requests?: K8sResources;
|
|
91
|
+
/** Resource limits (maximum resources) */
|
|
92
|
+
limits?: K8sResources;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Health check configuration for Kubernetes probes.
|
|
96
|
+
*/
|
|
97
|
+
export interface K8sHealthCheck {
|
|
98
|
+
/** HTTP path for health check (e.g., '/health') */
|
|
99
|
+
httpPath?: string;
|
|
100
|
+
/** Port for health check (defaults to containerPort) */
|
|
101
|
+
port?: number;
|
|
102
|
+
/** Initial delay before starting probes in seconds (default: 10) */
|
|
103
|
+
initialDelaySeconds?: number;
|
|
104
|
+
/** Period between probes in seconds (default: 10) */
|
|
105
|
+
periodSeconds?: number;
|
|
106
|
+
/** Number of consecutive failures before unhealthy (default: 3) */
|
|
107
|
+
failureThreshold?: number;
|
|
108
|
+
/** Number of consecutive successes to be healthy (default: 1) */
|
|
109
|
+
successThreshold?: number;
|
|
110
|
+
/** Probe timeout in seconds (default: 5) */
|
|
111
|
+
timeoutSeconds?: number;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Ingress configuration for public services.
|
|
115
|
+
*/
|
|
116
|
+
export interface K8sIngressConfig {
|
|
117
|
+
/** Path prefix for routing (e.g., '/', '/api') */
|
|
118
|
+
path: string;
|
|
119
|
+
/** Path type: 'Prefix' (default) or 'Exact' */
|
|
120
|
+
pathType?: 'Prefix' | 'Exact';
|
|
121
|
+
/** Custom annotations for the ingress */
|
|
122
|
+
annotations?: Record<string, string>;
|
|
123
|
+
/** Hostname for the ingress (required for TLS) */
|
|
124
|
+
host?: string;
|
|
125
|
+
/** Additional hostnames (e.g., www subdomain) */
|
|
126
|
+
additionalHosts?: string[];
|
|
127
|
+
/** TLS configuration */
|
|
128
|
+
tls?: {
|
|
129
|
+
/** Enable TLS (requires cert-manager ClusterIssuer) */
|
|
130
|
+
enabled: boolean;
|
|
131
|
+
/** ClusterIssuer name (default: 'letsencrypt-production') */
|
|
132
|
+
issuerName?: string;
|
|
133
|
+
/** Secret name for the TLS certificate (auto-generated if not provided) */
|
|
134
|
+
secretName?: string;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Volume mount configuration for persistent storage.
|
|
139
|
+
*/
|
|
140
|
+
export interface K8sVolumeMount {
|
|
141
|
+
/** Name of the volume/PVC */
|
|
142
|
+
name: string;
|
|
143
|
+
/** Mount path inside the container */
|
|
144
|
+
mountPath: string;
|
|
145
|
+
/** Size of the volume (e.g., '10Gi') */
|
|
146
|
+
size?: string;
|
|
147
|
+
/** Storage class (defaults to 'do-block-storage') */
|
|
148
|
+
storageClass?: string;
|
|
149
|
+
/** Access mode (defaults to 'ReadWriteOnce') */
|
|
150
|
+
accessMode?: 'ReadWriteOnce' | 'ReadOnlyMany' | 'ReadWriteMany';
|
|
151
|
+
/** Whether the volume is read-only */
|
|
152
|
+
readOnly?: boolean;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Configuration for a Kubernetes service deployment.
|
|
156
|
+
*
|
|
157
|
+
* This is the main interface for defining services in `infra/services/*.ts`.
|
|
158
|
+
* Each service config is translated into Kubernetes Deployment, Service,
|
|
159
|
+
* and optionally Ingress resources.
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* // Internal service (not publicly accessible)
|
|
164
|
+
* const config: K8sServiceConfig = {
|
|
165
|
+
* name: 'orders',
|
|
166
|
+
* image: 'ghcr.io/orderboss/platform/orders:latest',
|
|
167
|
+
* containerPort: 4001,
|
|
168
|
+
* replicas: 2,
|
|
169
|
+
* env: {
|
|
170
|
+
* PUBLIC_SUPABASE_URL: supabaseUrl,
|
|
171
|
+
* },
|
|
172
|
+
* secrets: {
|
|
173
|
+
* SUPABASE_SERVICE_ROLE_KEY: supabaseServiceRoleKey,
|
|
174
|
+
* },
|
|
175
|
+
* }
|
|
176
|
+
*
|
|
177
|
+
* // Public service with ingress
|
|
178
|
+
* const config: K8sServiceConfig = {
|
|
179
|
+
* name: 'storefront',
|
|
180
|
+
* image: 'ghcr.io/orderboss/platform/storefront:latest',
|
|
181
|
+
* containerPort: 3000,
|
|
182
|
+
* ingress: { path: '/' },
|
|
183
|
+
* healthCheck: { httpPath: '/health' },
|
|
184
|
+
* }
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
export interface K8sServiceConfig {
|
|
188
|
+
/** Unique name of the service (used for deployment, service, and labels) */
|
|
189
|
+
name: string;
|
|
190
|
+
/** Container image (e.g., 'ghcr.io/orderboss/platform/storefront:latest') */
|
|
191
|
+
image: string;
|
|
192
|
+
/** Port the container listens on */
|
|
193
|
+
containerPort: number;
|
|
194
|
+
/** Number of replicas (defaults to 1) */
|
|
195
|
+
replicas?: number;
|
|
196
|
+
/** Environment variables (plain values) */
|
|
197
|
+
env?: Record<string, pulumi.Input<string>>;
|
|
198
|
+
/** Secret environment variables (stored in K8s Secret) */
|
|
199
|
+
secrets?: Record<string, pulumi.Input<string>>;
|
|
200
|
+
/** Ingress configuration (set to enable public access) */
|
|
201
|
+
ingress?: K8sIngressConfig;
|
|
202
|
+
/** Health check configuration for liveness/readiness probes */
|
|
203
|
+
healthCheck?: K8sHealthCheck;
|
|
204
|
+
/** Resource requests and limits */
|
|
205
|
+
resources?: K8sResourceConfig;
|
|
206
|
+
/** Volume mounts for persistent storage */
|
|
207
|
+
volumes?: K8sVolumeMount[];
|
|
208
|
+
/** Command to run (overrides container entrypoint) */
|
|
209
|
+
command?: string[];
|
|
210
|
+
/** Arguments to pass to the command */
|
|
211
|
+
args?: string[];
|
|
212
|
+
/** Additional labels to apply to all resources */
|
|
213
|
+
labels?: Record<string, string>;
|
|
214
|
+
/** Additional annotations to apply to pods */
|
|
215
|
+
annotations?: Record<string, string>;
|
|
216
|
+
/** Service type: 'ClusterIP' (default), 'NodePort', or 'LoadBalancer' */
|
|
217
|
+
serviceType?: 'ClusterIP' | 'NodePort' | 'LoadBalancer';
|
|
218
|
+
/** Skip deployment (useful for temporarily disabling a service) */
|
|
219
|
+
skip?: boolean;
|
|
220
|
+
/** Image pull policy (defaults to 'Always' for :latest, 'IfNotPresent' otherwise) */
|
|
221
|
+
imagePullPolicy?: 'Always' | 'Never' | 'IfNotPresent';
|
|
222
|
+
/** Additional ports to expose (for services with multiple ports) */
|
|
223
|
+
additionalPorts?: Array<{
|
|
224
|
+
name: string;
|
|
225
|
+
port: number;
|
|
226
|
+
targetPort?: number;
|
|
227
|
+
protocol?: 'TCP' | 'UDP';
|
|
228
|
+
}>;
|
|
229
|
+
/** Name of the imagePullSecret to use for private registries */
|
|
230
|
+
imagePullSecretName?: string;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Options for deploying multiple K8s services.
|
|
234
|
+
*/
|
|
235
|
+
export interface DeployK8sServicesOptions {
|
|
236
|
+
/** Image pull secret name (applied to all services) */
|
|
237
|
+
imagePullSecretName?: string;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Configuration for NATS deployment with JetStream via Helm.
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```typescript
|
|
244
|
+
* const nats = deployNats(provider, 'orderboss', {
|
|
245
|
+
* replicas: 3,
|
|
246
|
+
* jetstream: {
|
|
247
|
+
* enabled: true,
|
|
248
|
+
* storageSize: '20Gi',
|
|
249
|
+
* },
|
|
250
|
+
* auth: {
|
|
251
|
+
* enabled: true,
|
|
252
|
+
* user: natsUser,
|
|
253
|
+
* password: natsPassword,
|
|
254
|
+
* },
|
|
255
|
+
* })
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
export interface NatsConfig {
|
|
259
|
+
/** Number of NATS server replicas (defaults to 3 for HA) */
|
|
260
|
+
replicas?: number;
|
|
261
|
+
/** JetStream storage configuration */
|
|
262
|
+
jetstream?: {
|
|
263
|
+
/** Enable JetStream (defaults to true) */
|
|
264
|
+
enabled?: boolean;
|
|
265
|
+
/** Storage size for file storage (e.g., '20Gi') */
|
|
266
|
+
storageSize?: string;
|
|
267
|
+
/** Storage class (defaults to 'do-block-storage') */
|
|
268
|
+
storageClass?: string;
|
|
269
|
+
/** Memory storage size (e.g., '1Gi') */
|
|
270
|
+
memoryStorageSize?: string;
|
|
271
|
+
};
|
|
272
|
+
/** Authentication configuration */
|
|
273
|
+
auth?: {
|
|
274
|
+
/** Enable authentication (defaults to false) */
|
|
275
|
+
enabled?: boolean;
|
|
276
|
+
/** Username for client connections */
|
|
277
|
+
user?: pulumi.Input<string>;
|
|
278
|
+
/** Password for client connections */
|
|
279
|
+
password?: pulumi.Input<string>;
|
|
280
|
+
};
|
|
281
|
+
/** Resource configuration */
|
|
282
|
+
resources?: K8sResourceConfig;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Deployment result for a K8s service.
|
|
286
|
+
*/
|
|
287
|
+
export interface K8sServiceDeploymentResult {
|
|
288
|
+
/** The Kubernetes Deployment resource */
|
|
289
|
+
deployment: unknown;
|
|
290
|
+
/** The Kubernetes Service resource */
|
|
291
|
+
service: unknown;
|
|
292
|
+
/** The Kubernetes Ingress resource (if public) */
|
|
293
|
+
ingress?: unknown;
|
|
294
|
+
/** The Kubernetes Secret resource (if secrets are defined) */
|
|
295
|
+
secret?: unknown;
|
|
296
|
+
/** PVCs for persistent storage */
|
|
297
|
+
pvcs?: unknown[];
|
|
298
|
+
/** Internal service URL (e.g., 'http://orders.orderboss.svc.cluster.local:4001') */
|
|
299
|
+
internalUrl: pulumi.Output<string>;
|
|
300
|
+
/** Service DNS name within cluster */
|
|
301
|
+
serviceDns: string;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Result of deploying NATS.
|
|
305
|
+
*/
|
|
306
|
+
export interface NatsDeploymentResult {
|
|
307
|
+
/** Helm release */
|
|
308
|
+
release: unknown;
|
|
309
|
+
/** Internal NATS URL for service-to-service communication */
|
|
310
|
+
internalUrl: string;
|
|
311
|
+
/** Service DNS name */
|
|
312
|
+
serviceDns: string;
|
|
313
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as digitalocean from '@pulumi/digitalocean';
|
|
2
|
+
import type { VPCConfig } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a DigitalOcean VPC for private networking.
|
|
5
|
+
*
|
|
6
|
+
* All DOKS nodes and services will use this VPC for internal communication.
|
|
7
|
+
* The VPC name is automatically suffixed with the current stack name.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const vpc = createVPC({
|
|
12
|
+
* name: 'orderboss-vpc',
|
|
13
|
+
* region: 'fra1',
|
|
14
|
+
* description: 'VPC for orderboss platform',
|
|
15
|
+
* })
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function createVPC(config: VPCConfig): digitalocean.Vpc;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as k8s from '@pulumi/kubernetes';
|
|
2
|
+
import * as pulumi from '@pulumi/pulumi';
|
|
3
|
+
import type { DeployK8sServicesOptions, K8sServiceConfig, K8sServiceDeploymentResult } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Creates an ImagePullSecret for private container registries (e.g., GHCR, DockerHub).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const secret = createImagePullSecret(provider, 'orderboss', 'ghcr-secret', {
|
|
10
|
+
* registry: 'ghcr.io',
|
|
11
|
+
* username: 'my-org',
|
|
12
|
+
* password: ghcrToken,
|
|
13
|
+
* })
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare function createImagePullSecret(provider: k8s.Provider, namespace: string, name: string, config: {
|
|
17
|
+
registry: string;
|
|
18
|
+
username: pulumi.Input<string>;
|
|
19
|
+
password: pulumi.Input<string>;
|
|
20
|
+
}): k8s.core.v1.Secret;
|
|
21
|
+
/**
|
|
22
|
+
* Deploys a service to Kubernetes.
|
|
23
|
+
*
|
|
24
|
+
* Creates:
|
|
25
|
+
* - Deployment with configurable replicas, resources, and health checks
|
|
26
|
+
* - Service (ClusterIP by default) for internal access
|
|
27
|
+
* - Secret (if `secrets` are defined)
|
|
28
|
+
* - PVCs (if `volumes` are defined)
|
|
29
|
+
* - Ingress (if `ingress` is defined) - Note: requires ingress controller
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const result = deployK8sService(provider, 'orderboss', {
|
|
34
|
+
* name: 'orders',
|
|
35
|
+
* image: 'ghcr.io/orderboss/platform/orders:latest',
|
|
36
|
+
* containerPort: 4001,
|
|
37
|
+
* replicas: 2,
|
|
38
|
+
* env: {
|
|
39
|
+
* PUBLIC_SUPABASE_URL: supabaseUrl,
|
|
40
|
+
* },
|
|
41
|
+
* secrets: {
|
|
42
|
+
* SUPABASE_SERVICE_ROLE_KEY: supabaseServiceRoleKey,
|
|
43
|
+
* },
|
|
44
|
+
* healthCheck: {
|
|
45
|
+
* httpPath: '/health',
|
|
46
|
+
* },
|
|
47
|
+
* })
|
|
48
|
+
*
|
|
49
|
+
* // Access the service internally:
|
|
50
|
+
* // http://orders.orderboss.svc.cluster.local:4001
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function deployK8sService(provider: k8s.Provider, namespace: string, config: K8sServiceConfig): K8sServiceDeploymentResult;
|
|
54
|
+
/**
|
|
55
|
+
* Deploys multiple services to Kubernetes.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const results = deployK8sServices(provider, 'orderboss', [
|
|
60
|
+
* ordersConfig,
|
|
61
|
+
* storefrontConfig,
|
|
62
|
+
* apiGatewayConfig,
|
|
63
|
+
* ], { imagePullSecretName: 'ghcr-secret' })
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function deployK8sServices(provider: k8s.Provider, namespace: string, configs: K8sServiceConfig[], options?: DeployK8sServicesOptions): Map<string, K8sServiceDeploymentResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Creates a Kubernetes namespace.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const ns = createNamespace(provider, 'orderboss')
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function createNamespace(provider: k8s.Provider, name: string, labels?: Record<string, string>): k8s.core.v1.Namespace;
|
|
76
|
+
/**
|
|
77
|
+
* Builds an internal service URL for cluster-internal communication.
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildInternalUrl(serviceName: string, namespace: string, port: number): string;
|