import * as k8s from '@pulumi/kubernetes' import * as pulumi from '@pulumi/pulumi' import * as dns from 'dns' /** * ServiceDeployment is an example abstraction that uses a class to fold together the common pattern of a * Kubernetes Deployment and its associated Service object. */ export class CaddyServiceDeployment extends pulumi.ComponentResource { public readonly deployment: k8s.apps.v1.Deployment public readonly service: k8s.core.v1.Service public readonly hostname?: pulumi.Output public readonly primaryEndpoint?: pulumi.Output public readonly secondaryEndpoint?: pulumi.Output constructor(name: string, args: ServiceDeploymentArgs, opts?: pulumi.ComponentResourceOptions) { super('caddy:service:CaddyServiceDeployment', name, {}, opts) const labels = { app: name } let volumes: pulumi.Input[]> = [] let caddyVolumeMounts: pulumi.Input[]> = [] async function lookupPromise(url: string): Promise { return new Promise((resolve, reject) => { dns.lookup(url, { all: true }, (err: any, addresses: dns.LookupAddress[]) => { if (err) reject(err) resolve(addresses) }) }) } this.service = new k8s.core.v1.Service( name, { metadata: { name: name, namespace: args.namespaceName, labels: labels, }, spec: { type: args.isMinikube ? 'NodePort' : 'LoadBalancer', ports: [ { name: 'http', port: 80 }, { name: 'https', port: 443 }, ], selector: labels, }, }, { parent: this } ) this.hostname = this.service.status.loadBalancer.ingress[0].hostname if (args.lbReady) { let caddyConfig: pulumi.Output const lbIps: pulumi.Output = this.hostname.apply((dnsName) => { return lookupPromise(dnsName) }) function getProxyString(ipAddress: pulumi.Output) { let result: pulumi.Output = pulumi.interpolate`` for (const endpoint of args.caddyEndpoints) { result = pulumi.interpolate`${ipAddress}.nip.io${endpoint}\n${result}` } return result } caddyConfig = pulumi.interpolate`${getProxyString(lbIps[0].address)} ${getProxyString(lbIps[1].address)}` this.primaryEndpoint = pulumi.interpolate`${lbIps[0].address}.nip.io` this.secondaryEndpoint = pulumi.interpolate`${lbIps[1].address}.nip.io` const keyConfig = new k8s.core.v1.ConfigMap( name, { metadata: { namespace: args.namespaceName, labels: labels }, data: { 'fileData': caddyConfig }, }, { parent: this } ) const keyConfigName = keyConfig.metadata.apply((m) => m.name) caddyVolumeMounts.push({ mountPath: '/etc/caddy/Caddyfile', name: 'caddy-volume', subPath: 'fileData', }) volumes.push({ name: 'caddy-volume', configMap: { name: keyConfigName, }, }) } this.deployment = new k8s.apps.v1.Deployment( name, { metadata: { namespace: args.namespaceName, labels: labels }, spec: { selector: { matchLabels: labels }, replicas: 1, template: { metadata: { labels: labels }, spec: { containers: [ { name: 'caddy', image: 'caddy', ports: [ { name: 'caddy-http', containerPort: 80 }, { name: 'caddy-https', containerPort: 443 }, ], volumeMounts: caddyVolumeMounts, }, ], volumes, }, }, }, }, { parent: this } ) } } export interface ServiceDeploymentArgs { namespaceName: pulumi.Output // Endpoints are caddyConfig strings concatenated after IP.nip.io caddyEndpoints: string[] lbReady?: boolean isMinikube?: boolean }