mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
212 lines
5.6 KiB
TypeScript
212 lines
5.6 KiB
TypeScript
import * as kx from '@pulumi/kubernetesx';
|
|
import * as k8s from '@pulumi/kubernetes';
|
|
import * as azure from '@pulumi/azure';
|
|
import * as pulumi from '@pulumi/pulumi';
|
|
import { PodBuilder, normalizeEnv } from './pod-builder';
|
|
import { PackageInfo } from './pack';
|
|
import { isDefined } from './helpers';
|
|
|
|
const DEFAULT_IMAGE = 'node:16.13.2-alpine3.15';
|
|
|
|
export class RemoteArtifactAsServiceDeployment {
|
|
constructor(
|
|
protected name: string,
|
|
protected options: {
|
|
storageContainer: azure.storage.Container;
|
|
env?: kx.types.Container['env'];
|
|
packageInfo: PackageInfo;
|
|
port?: number;
|
|
image?: string;
|
|
livenessProbe?: string;
|
|
readinessProbe?: string;
|
|
memoryLimit?: string;
|
|
cpuLimit?: string;
|
|
bin?: string;
|
|
/**
|
|
* Enables /metrics endpoint on port 10254
|
|
*/
|
|
exposesMetrics?: boolean;
|
|
replicas?: number;
|
|
},
|
|
protected dependencies?: Array<pulumi.Resource | undefined | null>,
|
|
protected parent?: pulumi.Resource | null
|
|
) {}
|
|
|
|
deployAsJob() {
|
|
const artifactUrl = this.makeArtifactUrl();
|
|
const { pb } = this.createPod(artifactUrl, true);
|
|
|
|
const job = new kx.Job(
|
|
this.name,
|
|
{
|
|
spec: pb.asJobSpec(),
|
|
},
|
|
{ dependsOn: this.dependencies?.filter(isDefined) }
|
|
);
|
|
|
|
return { job };
|
|
}
|
|
|
|
createPod(artifactUrl: pulumi.Output<string>, asJob: boolean) {
|
|
const port = this.options.port || 3000;
|
|
const additionalEnv: any[] = normalizeEnv(this.options.env);
|
|
|
|
let livenessProbe: k8s.types.input.core.v1.Probe | undefined = undefined;
|
|
let readinessProbe: k8s.types.input.core.v1.Probe | undefined = undefined;
|
|
|
|
if (this.options.livenessProbe) {
|
|
livenessProbe = {
|
|
initialDelaySeconds: 3,
|
|
periodSeconds: 20,
|
|
failureThreshold: 10,
|
|
timeoutSeconds: 5,
|
|
httpGet: {
|
|
path: this.options.livenessProbe,
|
|
port,
|
|
},
|
|
};
|
|
}
|
|
|
|
if (this.options.readinessProbe) {
|
|
readinessProbe = {
|
|
initialDelaySeconds: 5,
|
|
periodSeconds: 20,
|
|
failureThreshold: 5,
|
|
timeoutSeconds: 5,
|
|
httpGet: {
|
|
path: this.options.readinessProbe,
|
|
port,
|
|
},
|
|
};
|
|
}
|
|
|
|
const image = this.options.image || DEFAULT_IMAGE;
|
|
const appVolume = {
|
|
mountPath: '/app',
|
|
name: 'app',
|
|
};
|
|
|
|
const volumeMounts = [appVolume];
|
|
|
|
if (this.options.exposesMetrics) {
|
|
additionalEnv.push({ name: 'METRICS_ENABLED', value: 'true' });
|
|
}
|
|
|
|
const pb = new PodBuilder({
|
|
restartPolicy: asJob ? 'Never' : 'Always',
|
|
volumes: [
|
|
{
|
|
name: appVolume.name,
|
|
emptyDir: {},
|
|
},
|
|
],
|
|
initContainers: [
|
|
{
|
|
name: `${this.name}-init`,
|
|
image,
|
|
workingDir: appVolume.mountPath,
|
|
volumeMounts,
|
|
command:
|
|
this.options.packageInfo.runtime === 'node'
|
|
? ['/bin/sh', '-c', artifactUrl.apply((v) => `yarn add ${v}`)]
|
|
: this.options.packageInfo.runtime === 'rust'
|
|
? ['/bin/sh', '-c', artifactUrl.apply((v) => `wget ${v}`)]
|
|
: ['echo missing script!'],
|
|
},
|
|
],
|
|
containers: [
|
|
{
|
|
livenessProbe,
|
|
readinessProbe,
|
|
env: [
|
|
{ name: 'PORT', value: String(port) },
|
|
{
|
|
name: 'POD_NAME',
|
|
valueFrom: {
|
|
fieldRef: {
|
|
fieldPath: 'metadata.name',
|
|
},
|
|
},
|
|
},
|
|
].concat(additionalEnv),
|
|
name: this.name,
|
|
image,
|
|
workingDir: appVolume.mountPath,
|
|
volumeMounts: [appVolume],
|
|
command:
|
|
this.options.packageInfo.runtime === 'node'
|
|
? ['yarn', this.options.bin || this.options.packageInfo.bin]
|
|
: this.options.packageInfo.runtime === 'rust'
|
|
? [this.options.packageInfo.bin]
|
|
: [],
|
|
ports: {
|
|
http: port,
|
|
...(this.options.exposesMetrics
|
|
? {
|
|
metrics: 10254,
|
|
}
|
|
: {}),
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
return { pb };
|
|
}
|
|
|
|
private makeArtifactUrl() {
|
|
const azureStaticFile = new azure.storage.Blob(`${this.name}-artifact`, {
|
|
storageAccountName: this.options.storageContainer.storageAccountName,
|
|
storageContainerName: this.options.storageContainer.name,
|
|
type: 'Block',
|
|
source: new pulumi.asset.FileAsset(this.options.packageInfo.file),
|
|
});
|
|
|
|
return azureStaticFile.url;
|
|
}
|
|
|
|
deploy() {
|
|
const artifactUrl = this.makeArtifactUrl();
|
|
const { pb } = this.createPod(artifactUrl, false);
|
|
|
|
const metadata: k8s.types.input.meta.v1.ObjectMeta = {
|
|
annotations: {},
|
|
};
|
|
|
|
if (this.options.exposesMetrics) {
|
|
metadata.annotations = {
|
|
'prometheus.io/port': '10254',
|
|
'prometheus.io/path': '/metrics',
|
|
'prometheus.io/scrape': 'true',
|
|
};
|
|
}
|
|
|
|
const deployment = new kx.Deployment(
|
|
this.name,
|
|
{
|
|
spec: pb.asExtendedDeploymentSpec(
|
|
{
|
|
replicas: this.options.replicas ?? 1,
|
|
strategy: {
|
|
type: 'RollingUpdate',
|
|
rollingUpdate: {
|
|
maxSurge: this.options.replicas ?? 1,
|
|
maxUnavailable: 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
annotations: metadata.annotations,
|
|
}
|
|
),
|
|
},
|
|
{
|
|
dependsOn: this.dependencies?.filter(isDefined),
|
|
parent: this.parent ?? undefined,
|
|
}
|
|
);
|
|
const service = deployment.createService({});
|
|
|
|
return { deployment, service };
|
|
}
|
|
}
|