// index.ts import * as pulumi from "@pulumi/pulumi"; import { Output } from "@pulumi/pulumi"; import * as docker from "@pulumi/docker"; export type DockerDeploymentsContainerArgs = { name: string; providerName: string; image: string; ports?: { internal: number; external: number; protocol?: "tcp" | "udp"; ip?: string; // hostname?: string; }[]; volumes?: { source: string; target: string; readOnly?: boolean; type?: "bind" | "volume" | "tmpfs"; }[]; networks?: { network: string; aliases?: string[]; }[]; envs?: (string | Output)[]; config: Partial; providerConfig?: Partial; }; interface DockerDeploymentsArgs { provider: Record< string, { host: pulumi.Input; sshPrivateKeyPath?: string; networks?: Record; } & Partial >; containers: DockerDeploymentsContainerArgs[]; } export class DockerDeployments extends pulumi.ComponentResource { private providers: Record; public containers: { container: docker.Container; image: Output; volumes?: docker.Volume[]; }[]; private networks: Record; constructor( name: string, args: DockerDeploymentsArgs, opts?: pulumi.ComponentResourceOptions, ) { super("custom:resource:DockerDeployments", name, {}, opts); this.providers = Object.entries(args.provider).reduce( (acc, [providerName, providerConfig]) => { acc[providerName] = new docker.Provider( `${name}-${providerName}`, { host: providerConfig.host, sshOpts: providerConfig.sshPrivateKeyPath ? [ "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-i", providerConfig.sshPrivateKeyPath, ] : undefined, registryAuth: providerConfig.registryAuth, }, { parent: this, }, ); return acc; }, {} as Record, ); this.networks = {}; Object.entries(args.provider).forEach(([providerName, providerConfig]) => { if (providerConfig.networks) { Object.entries(providerConfig.networks).forEach( ([name, networkConfig]) => { this.networks[`${providerName}-${name}`] = new docker.Network( `${providerName}-${name}`, { name: name, attachable: true, driver: "bridge", internal: false, ...networkConfig, }, { parent: this, provider: this.providers[providerName], deleteBeforeReplace: true, }, ); }, ); } }); this.containers = []; for (const containerConfig of args.containers) { const provider = this.providers[containerConfig.providerName]; const containerImage = pulumi.output( docker.getRegistryImage( { name: containerConfig.image, }, { parent: this, provider: provider, }, ), ); const container = new docker.Container( `${containerConfig.providerName}-${containerConfig.name}`, { image: containerImage.apply((image) => image.name), name: containerConfig.name, hostname: containerConfig.name, ports: containerConfig.ports?.map((port) => ({ internal: port.internal, external: port.external, protocol: port.protocol || "tcp", ip: port.ip, })), mounts: containerConfig.volumes?.map((volume) => ({ source: volume.source, target: volume.target, readOnly: volume.readOnly || false, type: volume.type || "volume", })), networksAdvanced: containerConfig.networks?.map((network) => ({ name: network.network, aliases: network.aliases || [containerConfig.name], })), envs: containerConfig.envs ? [ ...containerConfig.envs, pulumi.interpolate`IMAGE_ID=${containerImage.apply((image) => image.id)}`, ] : [ pulumi.interpolate`IMAGE_ID=${containerImage.apply((image) => image.id)}`, ], ...containerConfig.config, }, { parent: this, provider: provider, ...containerConfig.providerConfig, ignoreChanges: containerConfig.providerConfig?.ignoreChanges ? [...containerConfig.providerConfig.ignoreChanges, "image"] : ["image"], deleteBeforeReplace: true, dependsOn: containerConfig.networks && containerConfig.networks[0].network ? [this.networks[containerConfig.networks[0].network]] : [], }, ); this.containers.push({ image: containerImage, container: container, }); } } }