Bridge terraform-provider-dokploy into a Pulumi provider
18 resources and 5 data sources mapped into the dokploy:index module, with SDKs generated for TypeScript, Python, Go and .NET.
This commit is contained in:
9
examples/typescript/Pulumi.yaml
Normal file
9
examples/typescript/Pulumi.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
name: dokploy-complete
|
||||
runtime: nodejs
|
||||
description: A complete Dokploy environment built with Pulumi
|
||||
config:
|
||||
dokploy-complete:githubProviderId:
|
||||
description: ID of the GitHub provider configured in Dokploy
|
||||
dokploy-complete:adminPassword:
|
||||
description: Basic-auth password for the staging admin tool
|
||||
secret: true
|
||||
148
examples/typescript/index.ts
Normal file
148
examples/typescript/index.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
// A complete Dokploy environment: a project, a staging environment, a public
|
||||
// web service with a database and a cache behind it, and a Compose stack.
|
||||
//
|
||||
// export DOKPLOY_HOST=https://dokploy.example.com
|
||||
// export DOKPLOY_API_KEY=...
|
||||
// pulumi up
|
||||
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import * as random from "@pulumi/random";
|
||||
import * as dokploy from "@maxvojtkov/pulumi-dokploy";
|
||||
|
||||
import { WebService } from "./webService";
|
||||
|
||||
const config = new pulumi.Config();
|
||||
const githubProviderId = config.require("githubProviderId");
|
||||
const adminPassword = config.requireSecret("adminPassword");
|
||||
|
||||
// ---------------------------------------------------------------- structure
|
||||
|
||||
const shop = new dokploy.Project("shop", {
|
||||
name: "shop",
|
||||
description: "Storefront and its backing services",
|
||||
// Shared by every service in the project.
|
||||
env: "TZ=Europe/Bucharest",
|
||||
});
|
||||
|
||||
// Dokploy creates a default "production" environment with the project; add a
|
||||
// second one for staging.
|
||||
const staging = new dokploy.Environment("staging", {
|
||||
name: "staging",
|
||||
description: "Pre-production",
|
||||
projectId: shop.id,
|
||||
env: "LOG_LEVEL=debug",
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------ secrets
|
||||
|
||||
const postgresPassword = new random.RandomPassword("postgres", { length: 32, special: false });
|
||||
const redisPassword = new random.RandomPassword("redis", { length: 32, special: false });
|
||||
|
||||
// --------------------------------------------------------------- datastores
|
||||
|
||||
const db = new dokploy.Postgres("db", {
|
||||
name: "shop-db",
|
||||
environmentId: shop.defaultEnvironmentId,
|
||||
dockerImage: "postgres:16-alpine",
|
||||
databaseName: "shop",
|
||||
databaseUser: "shop",
|
||||
databasePassword: postgresPassword.result,
|
||||
memoryLimit: "1g",
|
||||
cpuLimit: "1",
|
||||
memoryReservation: "256m",
|
||||
});
|
||||
|
||||
const cache = new dokploy.Redis("cache", {
|
||||
name: "shop-cache",
|
||||
environmentId: shop.defaultEnvironmentId,
|
||||
dockerImage: "redis:7-alpine",
|
||||
databasePassword: redisPassword.result,
|
||||
memoryLimit: "256m",
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------- the web service
|
||||
|
||||
// Dokploy resolves service names on the shared Docker network, so the database
|
||||
// is reachable by its generated appName.
|
||||
const storefrontEnv = pulumi
|
||||
.all([db.appName, postgresPassword.result, cache.appName, redisPassword.result])
|
||||
.apply(([dbHost, dbPass, cacheHost, cachePass]) => [
|
||||
`DATABASE_URL=postgresql://shop:${dbPass}@${dbHost}:5432/shop`,
|
||||
`REDIS_URL=redis://:${cachePass}@${cacheHost}:6379`,
|
||||
"NODE_ENV=production",
|
||||
].join("\n"));
|
||||
|
||||
const storefront = new WebService("storefront", {
|
||||
environmentId: shop.defaultEnvironmentId,
|
||||
description: "Public storefront",
|
||||
github: {
|
||||
githubId: githubProviderId,
|
||||
owner: "acme",
|
||||
repository: "storefront",
|
||||
branch: "main",
|
||||
},
|
||||
env: storefrontEnv,
|
||||
replicas: 2,
|
||||
memoryLimit: "1g",
|
||||
cpuLimit: "1",
|
||||
domains: [{ host: "shop.example.com", port: 3000 }],
|
||||
});
|
||||
|
||||
new dokploy.Redirect("storefront-www", {
|
||||
applicationId: storefront.application.id,
|
||||
regex: String.raw`^https://www\.shop\.example\.com/(.*)`,
|
||||
replacement: "https://shop.example.com/${1}",
|
||||
permanent: true,
|
||||
});
|
||||
|
||||
new dokploy.Mount("storefront-uploads", {
|
||||
serviceId: storefront.application.id,
|
||||
serviceType: "application",
|
||||
type: "volume",
|
||||
mountPath: "/app/uploads",
|
||||
volumeName: "storefront-uploads",
|
||||
});
|
||||
|
||||
// ------------------------------- an internal tool behind basic auth (staging)
|
||||
|
||||
new WebService("admin", {
|
||||
environmentId: staging.id,
|
||||
dockerImage: "traefik/whoami:latest",
|
||||
domains: [{ host: "admin.staging.example.com", port: 80 }],
|
||||
basicAuth: { username: "ops", password: adminPassword },
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------- compose stack
|
||||
|
||||
const observability = new dokploy.Compose("observability", {
|
||||
name: "observability",
|
||||
environmentId: shop.defaultEnvironmentId,
|
||||
composeType: "docker-compose",
|
||||
sourceType: "raw",
|
||||
// Preserve volumes if this stack is ever destroyed.
|
||||
deleteVolumes: false,
|
||||
composeFile: `services:
|
||||
uptime:
|
||||
image: louislam/uptime-kuma:1
|
||||
volumes:
|
||||
- uptime-data:/app/data
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
uptime-data:
|
||||
`,
|
||||
});
|
||||
|
||||
new dokploy.Domain("observability", {
|
||||
composeId: observability.id,
|
||||
domainType: "compose",
|
||||
serviceName: "uptime",
|
||||
host: "status.example.com",
|
||||
port: 3001,
|
||||
https: true,
|
||||
certificateType: "letsencrypt",
|
||||
});
|
||||
|
||||
export const projectId = shop.id;
|
||||
export const stagingEnvironmentId = staging.id;
|
||||
export const storefrontUrl = "https://shop.example.com";
|
||||
12
examples/typescript/package.json
Normal file
12
examples/typescript/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "dokploy-complete",
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.19.130",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@maxvojtkov/pulumi-dokploy": "file:../../sdk/nodejs",
|
||||
"@pulumi/pulumi": "^3.256.0",
|
||||
"@pulumi/random": "^4.21.1"
|
||||
}
|
||||
}
|
||||
16
examples/typescript/tsconfig.json
Normal file
16
examples/typescript/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"outDir": "bin",
|
||||
"target": "es2020",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"pretty": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitReturns": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"files": ["index.ts", "webService.ts"]
|
||||
}
|
||||
93
examples/typescript/webService.ts
Normal file
93
examples/typescript/webService.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import * as pulumi from "@pulumi/pulumi";
|
||||
import * as dokploy from "@maxvojtkov/pulumi-dokploy";
|
||||
|
||||
export interface DomainSpec {
|
||||
host: string;
|
||||
port: number;
|
||||
https?: boolean;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
export interface WebServiceArgs {
|
||||
environmentId: pulumi.Input<string>;
|
||||
description?: pulumi.Input<string>;
|
||||
|
||||
/** Either a prebuilt image or a GitHub repository, not both. */
|
||||
dockerImage?: pulumi.Input<string>;
|
||||
github?: {
|
||||
githubId: pulumi.Input<string>;
|
||||
owner: pulumi.Input<string>;
|
||||
repository: pulumi.Input<string>;
|
||||
branch: pulumi.Input<string>;
|
||||
};
|
||||
|
||||
env?: pulumi.Input<string>;
|
||||
replicas?: pulumi.Input<number>;
|
||||
memoryLimit?: pulumi.Input<string>;
|
||||
cpuLimit?: pulumi.Input<string>;
|
||||
|
||||
domains?: DomainSpec[];
|
||||
/** username/password pair to put the whole service behind basic auth. */
|
||||
basicAuth?: { username: pulumi.Input<string>; password: pulumi.Input<string> };
|
||||
}
|
||||
|
||||
/**
|
||||
* WebService bundles an application with its domains and access control — the
|
||||
* Pulumi equivalent of the `web-service` Terraform module.
|
||||
*/
|
||||
export class WebService extends pulumi.ComponentResource {
|
||||
public readonly application: dokploy.Application;
|
||||
public readonly domains: dokploy.Domain[];
|
||||
|
||||
constructor(name: string, args: WebServiceArgs, opts?: pulumi.ComponentResourceOptions) {
|
||||
super("dokploy:index:WebService", name, {}, opts);
|
||||
const child = { parent: this };
|
||||
|
||||
this.application = new dokploy.Application(name, {
|
||||
name,
|
||||
environmentId: args.environmentId,
|
||||
description: args.description,
|
||||
env: args.env,
|
||||
replicas: args.replicas,
|
||||
memoryLimit: args.memoryLimit,
|
||||
cpuLimit: args.cpuLimit,
|
||||
...(args.github
|
||||
? {
|
||||
sourceType: "github",
|
||||
buildType: "nixpacks",
|
||||
autoDeploy: true,
|
||||
githubId: args.github.githubId,
|
||||
owner: args.github.owner,
|
||||
repository: args.github.repository,
|
||||
branch: args.github.branch,
|
||||
}
|
||||
: {
|
||||
sourceType: "docker",
|
||||
dockerImage: args.dockerImage,
|
||||
}),
|
||||
}, child);
|
||||
|
||||
this.domains = (args.domains ?? []).map((d, i) => new dokploy.Domain(`${name}-domain-${i}`, {
|
||||
applicationId: this.application.id,
|
||||
domainType: "application",
|
||||
host: d.host,
|
||||
port: d.port,
|
||||
path: d.path,
|
||||
https: d.https ?? true,
|
||||
certificateType: (d.https ?? true) ? "letsencrypt" : "none",
|
||||
}, child));
|
||||
|
||||
if (args.basicAuth) {
|
||||
new dokploy.Security(`${name}-auth`, {
|
||||
applicationId: this.application.id,
|
||||
username: args.basicAuth.username,
|
||||
password: args.basicAuth.password,
|
||||
}, child);
|
||||
}
|
||||
|
||||
this.registerOutputs({
|
||||
application: this.application,
|
||||
domains: this.domains,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user