unity-builder/src/model/kubernetes.ts

360 lines
12 KiB
TypeScript
Raw Normal View History

// @ts-ignore
2021-05-23 13:31:02 +00:00
import * as k8s from '@kubernetes/client-node';
import { BuildParameters } from '.';
const core = require('@actions/core');
2021-05-23 13:31:02 +00:00
// const base64 = require('base-64');
2021-05-23 13:31:02 +00:00
// const pollInterval = 10000;
class Kubernetes {
2021-05-23 13:31:02 +00:00
private static kubeClient: k8s.CoreV1Api;
private static buildId: string;
2021-05-23 13:31:02 +00:00
private static buildParameters: BuildParameters;
private static baseImage: any;
private static pvcName: string;
private static secretName: string;
private static jobName: string;
private static namespace: string;
2021-05-23 13:31:02 +00:00
static async runBuildJob(buildParameters: BuildParameters, baseImage) {
2021-05-23 13:11:06 +00:00
core.info('Starting up k8s');
const kc = new k8s.KubeConfig();
kc.loadFromDefault();
2021-05-23 13:31:02 +00:00
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
2021-05-23 13:11:06 +00:00
core.info('loaded from default');
2021-05-23 13:31:02 +00:00
// const kubeconfig = new KubeConfig();
// kubeconfig.loadFromString(base64.decode(buildParameters.kubeConfig));
// const backend = new Request({ kubeconfig });
// const kubeClient = new Client(backend);
// await kubeClient.loadSpec();
const buildId = Kubernetes.uuidv4();
const pvcName = `unity-builder-pvc-${buildId}`;
const secretName = `build-credentials-${buildId}`;
const jobName = `unity-builder-job-${buildId}`;
const namespace = 'default';
2021-05-23 13:31:02 +00:00
this.kubeClient = k8sApi;
this.buildId = buildId;
this.buildParameters = buildParameters;
this.baseImage = baseImage;
this.pvcName = pvcName;
this.secretName = secretName;
this.jobName = jobName;
this.namespace = namespace;
2021-05-23 13:31:02 +00:00
// await Kubernetes.createSecret();
// await Kubernetes.createPersistentVolumeClaim();
// await Kubernetes.scheduleBuildJob();
// await Kubernetes.watchBuildJobUntilFinished();
// await Kubernetes.cleanup();
core.setOutput('volume', pvcName);
}
2021-05-23 13:31:02 +00:00
// static async createSecret() {
// //const secretManifest = {
// // apiVersion: 'v1',
// // kind: 'Secret',
// // metadata: {
// // name: this.secretName,
// // },
// // type: 'Opaque',
// // data: {
// // GITHUB_TOKEN: base64.encode(this.buildParameters.githubToken),
// // UNITY_LICENSE: base64.encode(process.env.UNITY_LICENSE),
// // ANDROID_KEYSTORE_BASE64: base64.encode(this.buildParameters.androidKeystoreBase64),
// // ANDROID_KEYSTORE_PASS: base64.encode(this.buildParameters.androidKeystorePass),
// // ANDROID_KEYALIAS_PASS: base64.encode(this.buildParameters.androidKeyaliasPass),
// // },
// //};
// //await this.kubeClient.api.v1.namespaces(this.namespace).secrets.post({ body: secretManifest });
// }
2021-05-23 13:31:02 +00:00
// static async createPersistentVolumeClaim() {
// if (this.buildParameters.kubeVolume) {
// core.info(this.buildParameters.kubeVolume);
// this.pvcName = this.buildParameters.kubeVolume;
// return;
// }
// const pvcManifest = {
// apiVersion: 'v1',
// kind: 'PersistentVolumeClaim',
// metadata: {
// name: this.pvcName,
// },
// spec: {
// accessModes: ['ReadWriteOnce'],
// volumeMode: 'Filesystem',
// resources: {
// requests: {
// storage: this.buildParameters.kubeVolumeSize,
// },
// },
// },
// };
// await this.kubeClient.api.v1.namespaces(this.namespace).persistentvolumeclaims.post({ body: pvcManifest });
// core.info('Persistent Volume created, waiting for ready state...');
// await Kubernetes.watchPersistentVolumeClaimUntilReady();
// core.info('Persistent Volume ready for claims');
// }
2021-05-23 13:31:02 +00:00
// static async watchPersistentVolumeClaimUntilReady() {
// await new Promise((resolve) => setTimeout(resolve, pollInterval));
// const queryResult = await this.kubeClient.api.v1
// .namespaces(this.namespace)
// .persistentvolumeclaims(this.pvcName)
// .get();
// if (queryResult.body.status.phase === 'Pending') {
// await Kubernetes.watchPersistentVolumeClaimUntilReady();
// }
// }
2021-05-23 13:31:02 +00:00
// static async scheduleBuildJob() {
// core.info('Creating build job');
// const jobManifest = {
// apiVersion: 'batch/v1',
// kind: 'Job',
// metadata: {
// name: this.jobName,
// labels: {
// app: 'unity-builder',
// },
// },
// spec: {
// template: {
// backoffLimit: 1,
// spec: {
// volumes: [
// {
// name: 'data',
// persistentVolumeClaim: {
// claimName: this.pvcName,
// },
// },
// {
// name: 'credentials',
// secret: {
// secretName: this.secretName,
// },
// },
// ],
// initContainers: [
// {
// name: 'clone',
// image: 'alpine/git',
// command: [
// '/bin/sh',
// '-c',
// `apk update;
// apk add git-lfs;
// export GITHUB_TOKEN=$(cat /credentials/GITHUB_TOKEN);
// cd /data;
// git clone https://github.com/${process.env.GITHUB_REPOSITORY}.git repo;
// git clone https://github.com/webbertakken/unity-builder.git builder;
// cd repo;
// git checkout $GITHUB_SHA;
// ls`,
// ],
// volumeMounts: [
// {
// name: 'data',
// mountPath: '/data',
// },
// {
// name: 'credentials',
// mountPath: '/credentials',
// readOnly: true,
// },
// ],
// env: [
// {
// name: 'GITHUB_SHA',
// value: this.buildId,
// },
// ],
// },
// ],
// containers: [
// {
// name: 'main',
// image: `${this.baseImage.toString()}`,
// command: [
// 'bin/bash',
// '-c',
// `for f in ./credentials/*; do export $(basename $f)="$(cat $f)"; done
// cp -r /data/builder/action/default-build-script /UnityBuilderAction
// cp -r /data/builder/action/entrypoint.sh /entrypoint.sh
// cp -r /data/builder/action/steps /steps
// chmod -R +x /entrypoint.sh;
// chmod -R +x /steps;
// /entrypoint.sh;
// `,
// ],
// resources: {
// requests: {
// memory: this.buildParameters.kubeContainerMemory,
// cpu: this.buildParameters.kubeContainerCPU,
// },
// },
// env: [
// {
// name: 'GITHUB_WORKSPACE',
// value: '/data/repo',
// },
// {
// name: 'PROJECT_PATH',
// value: this.buildParameters.projectPath,
// },
// {
// name: 'BUILD_PATH',
// value: this.buildParameters.buildPath,
// },
// {
// name: 'BUILD_FILE',
// value: this.buildParameters.buildFile,
// },
// {
// name: 'BUILD_NAME',
// value: this.buildParameters.buildName,
// },
// {
// name: 'BUILD_METHOD',
// value: this.buildParameters.buildMethod,
// },
// {
// name: 'CUSTOM_PARAMETERS',
// value: this.buildParameters.customParameters,
// },
// {
// name: 'CHOWN_FILES_TO',
// value: this.buildParameters.chownFilesTo,
// },
// {
// name: 'BUILD_TARGET',
// value: this.buildParameters.platform,
// },
// {
// name: 'ANDROID_VERSION_CODE',
// value: this.buildParameters.androidVersionCode.toString(),
// },
// {
// name: 'ANDROID_KEYSTORE_NAME',
// value: this.buildParameters.androidKeystoreName,
// },
// {
// name: 'ANDROID_KEYALIAS_NAME',
// value: this.buildParameters.androidKeyaliasName,
// },
// ],
// volumeMounts: [
// {
// name: 'data',
// mountPath: '/data',
// },
// {
// name: 'credentials',
// mountPath: '/credentials',
// readOnly: true,
// },
// ],
// lifeCycle: {
// preStop: {
// exec: {
// command: [
// 'bin/bash',
// '-c',
// `cd /data/builder/action/steps;
// chmod +x /return_license.sh;
// /return_license.sh;`,
// ],
// },
// },
// },
// },
// ],
// restartPolicy: 'Never',
// },
// },
// },
// };
// await this.kubeClient.apis.batch.v1.namespaces(this.namespace).jobs.post({ body: jobManifest });
// core.info('Job created');
// }
2021-05-23 13:31:02 +00:00
// static async watchBuildJobUntilFinished() {
// let podname;
// let ready = false;
// while (!ready) {
// await new Promise((resolve) => setTimeout(resolve, pollInterval));
// const pods = await this.kubeClient.api.v1.namespaces(this.namespace).pods.get();
// for (let index = 0; index < pods.body.items.length; index++) {
// const element = pods.body.items[index];
// if (element.metadata.labels['job-name'] === this.jobName && element.status.phase !== 'Pending') {
// core.info('Pod no longer pending');
// if (element.status.phase === 'Failure') {
// core.error('Kubernetes job failed');
// } else {
// ready = true;
// podname = element.metadata.name;
// }
// }
// }
// }
2021-05-23 13:31:02 +00:00
// core.info(`Watching build job ${podname}`);
// let logQueryTime;
// let complete = false;
// while (!complete) {
// await new Promise((resolve) => setTimeout(resolve, pollInterval));
2021-05-23 13:31:02 +00:00
// const podStatus = await this.kubeClient.api.v1.namespaces(this.namespace).pod(podname).get();
// if (podStatus.body.status.phase !== 'Running') {
// complete = true;
// }
2021-05-23 13:31:02 +00:00
// const logs = await this.kubeClient.api.v1
// .namespaces(this.namespace)
// .pod(podname)
// .log.get({
// qs: {
// sinceTime: logQueryTime,
// timestamps: true,
// },
// });
// if (logs.body !== undefined) {
// const arrayOfLines = logs.body.match(/[^\n\r]+/g).reverse();
// for (const element of arrayOfLines) {
// const [time, ...line] = element.split(' ');
// if (time !== logQueryTime) {
// core.info(line.join(' '));
// } else {
// break;
// }
// }
2021-05-23 13:31:02 +00:00
// if (podStatus.body.status.phase === 'Failed') {
// throw new Error('Kubernetes job failed');
// }
2021-05-23 13:31:02 +00:00
// logQueryTime = arrayOfLines[0].split(' ')[0];
// }
// }
// }
2021-05-23 13:31:02 +00:00
// static async cleanup() {
// await this.kubeClient.apis.batch.v1.namespaces(this.namespace).jobs(this.jobName).delete();
// await this.kubeClient.api.v1.namespaces(this.namespace).secrets(this.secretName).delete();
// }
static uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.trunc(Math.random() * 16);
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
}
export default Kubernetes;