unity-builder/src/model/cloud-runner/k8s/index.ts

169 lines
6.1 KiB
TypeScript
Raw Normal View History

2021-05-23 13:31:02 +00:00
import * as k8s from '@kubernetes/client-node';
2021-09-22 20:05:21 +00:00
import { BuildParameters } from '../..';
2021-05-28 17:54:54 +00:00
import * as core from '@actions/core';
import { CloudRunnerProviderInterface } from '../services/cloud-runner-provider-interface';
import CloudRunnerSecret from '../services/cloud-runner-secret';
2021-06-18 19:52:07 +00:00
import KubernetesStorage from './kubernetes-storage';
import CloudRunnerEnvironmentVariable from '../services/cloud-runner-environment-variable';
import KubernetesLogging from './kubernetes-logging';
import KubernetesSecret from './kubernetes-secret';
import KubernetesUtilities from './kubernetes-utils';
2021-06-26 04:01:43 +00:00
import waitUntil from 'async-wait-until';
2021-06-26 01:50:03 +00:00
import KubernetesJobSpecFactory from './kubernetes-job-spec-factory';
2021-07-13 00:28:16 +00:00
import KubernetesServiceAccount from './kubernetes-service-account';
import CloudRunnerLogger from '../services/cloud-runner-logger';
2021-08-17 20:09:42 +00:00
class Kubernetes implements CloudRunnerProviderInterface {
private kubeConfig: k8s.KubeConfig;
2021-06-06 19:39:06 +00:00
private kubeClient: k8s.CoreV1Api;
private kubeClientBatch: k8s.BatchV1Api;
private buildGuid: string = '';
2021-06-06 19:39:06 +00:00
private buildParameters: BuildParameters;
2021-06-06 21:22:22 +00:00
private pvcName: string = '';
private secretName: string = '';
private jobName: string = '';
2021-06-06 19:39:06 +00:00
private namespace: string;
2021-06-06 20:14:12 +00:00
private podName: string = '';
private containerName: string = '';
2021-06-26 01:50:03 +00:00
private cleanupCronJobName: string = '';
2021-07-13 00:28:16 +00:00
private serviceAccountName: string = '';
2021-06-26 01:50:03 +00:00
private kubeClientBatchBeta: k8s.BatchV1beta1Api;
constructor(buildParameters: BuildParameters) {
2021-06-26 01:50:03 +00:00
this.kubeConfig = new k8s.KubeConfig();
this.kubeConfig.loadFromDefault();
this.kubeClient = this.kubeConfig.makeApiClient(k8s.CoreV1Api);
this.kubeClientBatch = this.kubeConfig.makeApiClient(k8s.BatchV1Api);
this.kubeClientBatchBeta = this.kubeConfig.makeApiClient(k8s.BatchV1beta1Api);
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log('Loaded default Kubernetes configuration for this environment');
2021-05-23 13:31:02 +00:00
2021-06-06 21:22:22 +00:00
this.namespace = 'default';
this.buildParameters = buildParameters;
}
2021-12-25 20:10:12 +00:00
public async setupSharedResources(
buildGuid: string,
buildParameters: BuildParameters,
// eslint-disable-next-line no-unused-vars
branchName: string,
// eslint-disable-next-line no-unused-vars
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
) {
2021-06-26 01:54:37 +00:00
try {
2021-09-08 20:21:15 +00:00
this.pvcName = `unity-builder-pvc-${buildGuid}`;
this.cleanupCronJobName = `unity-builder-cronjob-${buildGuid}`;
this.serviceAccountName = `service-account-${buildGuid}`;
2021-06-26 01:54:37 +00:00
await KubernetesStorage.createPersistentVolumeClaim(
buildParameters,
this.pvcName,
this.kubeClient,
this.namespace,
);
2021-07-13 00:28:16 +00:00
await KubernetesServiceAccount.createServiceAccount(this.serviceAccountName, this.namespace, this.kubeClient);
2021-06-26 01:54:37 +00:00
} catch (error) {
throw error;
}
}
2021-12-25 20:10:12 +00:00
async runTask(
buildGuid: string,
image: string,
2021-12-29 16:28:42 +00:00
commands: string,
mountdir: string,
workingdir: string,
2021-08-17 22:13:46 +00:00
environment: CloudRunnerEnvironmentVariable[],
secrets: CloudRunnerSecret[],
): Promise<void> {
try {
// setup
this.buildGuid = buildGuid;
this.secretName = `build-credentials-${buildGuid}`;
this.jobName = `unity-builder-job-${buildGuid}`;
2021-09-15 03:35:57 +00:00
this.containerName = `main`;
await KubernetesSecret.createSecret(secrets, this.secretName, this.namespace, this.kubeClient);
2021-06-26 01:50:03 +00:00
const jobSpec = KubernetesJobSpecFactory.getJobSpec(
commands,
image,
mountdir,
workingdir,
environment,
this.buildGuid,
2021-06-26 01:50:03 +00:00
this.buildParameters,
this.secretName,
this.pvcName,
this.jobName,
k8s,
);
//run
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log('Creating build job');
await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log('Job created');
this.setPodNameAndContainerName(
await KubernetesUtilities.findPodFromJob(this.kubeClient, this.jobName, this.namespace),
);
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log('Watching pod until running');
await KubernetesUtilities.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log('Pod running, streaming logs');
await KubernetesLogging.streamLogs(
this.kubeConfig,
this.kubeClient,
this.jobName,
this.podName,
2021-09-15 04:35:56 +00:00
'main',
this.namespace,
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log,
);
await this.cleanupTaskResources();
} catch (error) {
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log('Running job failed');
core.error(JSON.stringify(error, undefined, 4));
await this.cleanupTaskResources();
2021-06-06 20:10:01 +00:00
throw error;
}
}
2021-06-06 21:22:22 +00:00
setPodNameAndContainerName(pod: k8s.V1Pod) {
this.podName = pod.metadata?.name || '';
this.containerName = pod.status?.containerStatuses?.[0].name || '';
2021-05-28 17:37:30 +00:00
}
async cleanupTaskResources() {
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log('cleaning up');
2021-06-18 22:20:04 +00:00
try {
await this.kubeClientBatch.deleteNamespacedJob(this.jobName, this.namespace);
await this.kubeClient.deleteNamespacedSecret(this.secretName, this.namespace);
} catch (error) {
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log('Failed to cleanup, error:');
2021-06-18 22:20:04 +00:00
core.error(JSON.stringify(error, undefined, 4));
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log('Abandoning cleanup, build error:');
2021-06-26 03:27:42 +00:00
throw error;
2021-06-18 22:20:04 +00:00
}
2021-06-26 04:01:43 +00:00
try {
await waitUntil(
async () => (await this.kubeClientBatch.readNamespacedJob(this.jobName, this.namespace)).body === null,
{
timeout: 500000,
intervalBetweenAttempts: 15000,
},
);
} catch {
2021-09-21 18:27:04 +00:00
CloudRunnerLogger.log('failed to read the state of the job while cleaning up?');
2021-06-26 04:01:43 +00:00
}
}
2021-12-25 20:10:12 +00:00
async cleanupSharedResources(
// eslint-disable-next-line no-unused-vars
buildGuid: string,
// eslint-disable-next-line no-unused-vars
buildParameters: BuildParameters,
// eslint-disable-next-line no-unused-vars
branchName: string,
// eslint-disable-next-line no-unused-vars
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
) {
await this.kubeClient.deleteNamespacedPersistentVolumeClaim(this.pvcName, this.namespace);
}
}
export default Kubernetes;