PVC handling refactoring and logging
parent
09ed77c94a
commit
08db6c5022
|
|
@ -580,8 +580,8 @@ const unity_1 = __importDefault(__webpack_require__(70498));
|
||||||
exports.Unity = unity_1.default;
|
exports.Unity = unity_1.default;
|
||||||
const versioning_1 = __importDefault(__webpack_require__(88729));
|
const versioning_1 = __importDefault(__webpack_require__(88729));
|
||||||
exports.Versioning = versioning_1.default;
|
exports.Versioning = versioning_1.default;
|
||||||
const kubernetes_1 = __importDefault(__webpack_require__(7352));
|
const kubernetes_build_platform_1 = __importDefault(__webpack_require__(81730));
|
||||||
exports.Kubernetes = kubernetes_1.default;
|
exports.Kubernetes = kubernetes_build_platform_1.default;
|
||||||
const remote_builder_1 = __importDefault(__webpack_require__(49358));
|
const remote_builder_1 = __importDefault(__webpack_require__(49358));
|
||||||
exports.RemoteBuilder = remote_builder_1.default;
|
exports.RemoteBuilder = remote_builder_1.default;
|
||||||
|
|
||||||
|
|
@ -693,450 +693,6 @@ class Input {
|
||||||
exports.default = Input;
|
exports.default = Input;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
|
||||||
|
|
||||||
/***/ 7352:
|
|
||||||
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
||||||
}) : (function(o, m, k, k2) {
|
|
||||||
if (k2 === undefined) k2 = k;
|
|
||||||
o[k2] = m[k];
|
|
||||||
}));
|
|
||||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
||||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
||||||
}) : function(o, v) {
|
|
||||||
o["default"] = v;
|
|
||||||
});
|
|
||||||
var __importStar = (this && this.__importStar) || function (mod) {
|
|
||||||
if (mod && mod.__esModule) return mod;
|
|
||||||
var result = {};
|
|
||||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
||||||
__setModuleDefault(result, mod);
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
||||||
const k8s = __importStar(__webpack_require__(89679));
|
|
||||||
const core = __importStar(__webpack_require__(42186));
|
|
||||||
const client_node_1 = __webpack_require__(89679);
|
|
||||||
const stream_1 = __webpack_require__(92413);
|
|
||||||
const async_wait_until_1 = __webpack_require__(41299);
|
|
||||||
const base64 = __webpack_require__(85848);
|
|
||||||
class Kubernetes {
|
|
||||||
constructor(buildParameters, baseImage) {
|
|
||||||
this.buildId = '';
|
|
||||||
this.pvcName = '';
|
|
||||||
this.secretName = '';
|
|
||||||
this.jobName = '';
|
|
||||||
this.podName = '';
|
|
||||||
this.containerName = '';
|
|
||||||
const kc = new k8s.KubeConfig();
|
|
||||||
kc.loadFromDefault();
|
|
||||||
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
|
|
||||||
const k8sBatchApi = kc.makeApiClient(k8s.BatchV1Api);
|
|
||||||
core.info('Loaded default Kubernetes configuration for this environment');
|
|
||||||
this.kubeConfig = kc;
|
|
||||||
this.kubeClient = k8sApi;
|
|
||||||
this.kubeClientBatch = k8sBatchApi;
|
|
||||||
this.namespace = 'default';
|
|
||||||
this.buildParameters = buildParameters;
|
|
||||||
this.baseImage = baseImage;
|
|
||||||
this.setUniqueBuildId();
|
|
||||||
}
|
|
||||||
setUniqueBuildId() {
|
|
||||||
const buildId = Kubernetes.uuidv4();
|
|
||||||
const pvcName = `unity-builder-pvc-${buildId}`;
|
|
||||||
const secretName = `build-credentials-${buildId}`;
|
|
||||||
const jobName = `unity-builder-job-${buildId}`;
|
|
||||||
this.buildId = buildId;
|
|
||||||
this.pvcName = pvcName;
|
|
||||||
this.secretName = secretName;
|
|
||||||
this.jobName = jobName;
|
|
||||||
}
|
|
||||||
run() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
core.info('Running Remote Builder on Kubernetes');
|
|
||||||
const defaultSecretsArray = [
|
|
||||||
{
|
|
||||||
ParameterKey: 'GithubToken',
|
|
||||||
EnvironmentVariable: 'GITHUB_TOKEN',
|
|
||||||
ParameterValue: this.buildParameters.githubToken,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'UNITY_LICENSE',
|
|
||||||
EnvironmentVariable: 'UNITY_LICENSE',
|
|
||||||
ParameterValue: process.env.UNITY_LICENSE || '',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYSTORE_BASE64',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYSTORE_BASE64',
|
|
||||||
ParameterValue: this.buildParameters.androidKeystoreBase64,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYSTORE_PASS',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYSTORE_PASS',
|
|
||||||
ParameterValue: this.buildParameters.androidKeystorePass,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ParameterKey: 'ANDROID_KEYALIAS_PASS',
|
|
||||||
EnvironmentVariable: 'ANDROID_KEYALIAS_PASS',
|
|
||||||
ParameterValue: this.buildParameters.androidKeyaliasPass,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
try {
|
|
||||||
// setup
|
|
||||||
yield this.createSecret(defaultSecretsArray);
|
|
||||||
yield this.createPersistentVolumeClaim();
|
|
||||||
// run
|
|
||||||
yield this.runCloneJob();
|
|
||||||
yield this.runBuildJob();
|
|
||||||
yield this.cleanup();
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
core.error(JSON.stringify(error.response, undefined, 4));
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
core.setOutput('volume', this.pvcName);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
createSecret(secrets) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
const secret = new k8s.V1Secret();
|
|
||||||
secret.apiVersion = 'v1';
|
|
||||||
secret.kind = 'Secret';
|
|
||||||
secret.type = 'Opaque';
|
|
||||||
secret.metadata = {
|
|
||||||
name: this.secretName,
|
|
||||||
};
|
|
||||||
secret.data = {};
|
|
||||||
for (const buildSecret of secrets) {
|
|
||||||
secret.data[buildSecret.EnvironmentVariable] = base64.encode(buildSecret.ParameterValue);
|
|
||||||
secret.data[`${buildSecret.EnvironmentVariable}_NAME`] = base64.encode(buildSecret.ParameterKey);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
yield this.kubeClient.createNamespacedSecret(this.namespace, secret);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getPVCPhase() {
|
|
||||||
var _a;
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
return (_a = (yield this.kubeClient.readNamespacedPersistentVolumeClaimStatus(this.pvcName, this.namespace)).body.status) === null || _a === void 0 ? void 0 : _a.phase;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
createPersistentVolumeClaim() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (this.buildParameters.kubeVolume) {
|
|
||||||
core.info(this.buildParameters.kubeVolume);
|
|
||||||
this.pvcName = this.buildParameters.kubeVolume;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pvc = new k8s.V1PersistentVolumeClaim();
|
|
||||||
pvc.apiVersion = 'v1';
|
|
||||||
pvc.kind = 'PersistentVolumeClaim';
|
|
||||||
pvc.metadata = {
|
|
||||||
name: this.pvcName,
|
|
||||||
};
|
|
||||||
pvc.spec = {
|
|
||||||
accessModes: ['ReadWriteOnce'],
|
|
||||||
volumeMode: 'Filesystem',
|
|
||||||
resources: {
|
|
||||||
requests: {
|
|
||||||
storage: this.buildParameters.kubeVolumeSize,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
yield this.kubeClient.createNamespacedPersistentVolumeClaim(this.namespace, pvc);
|
|
||||||
core.info(`Persistent Volume created, ${yield this.getPVCPhase()}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getJobSpec(command, image) {
|
|
||||||
const job = new k8s.V1Job();
|
|
||||||
job.apiVersion = 'batch/v1';
|
|
||||||
job.kind = 'Job';
|
|
||||||
job.metadata = {
|
|
||||||
name: this.jobName,
|
|
||||||
labels: {
|
|
||||||
app: 'unity-builder',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
job.spec = {
|
|
||||||
template: {
|
|
||||||
spec: {
|
|
||||||
volumes: [
|
|
||||||
{
|
|
||||||
name: 'data',
|
|
||||||
persistentVolumeClaim: {
|
|
||||||
claimName: this.pvcName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'credentials',
|
|
||||||
secret: {
|
|
||||||
secretName: this.secretName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
containers: [
|
|
||||||
{
|
|
||||||
name: 'main',
|
|
||||||
image,
|
|
||||||
command,
|
|
||||||
resources: {
|
|
||||||
requests: {
|
|
||||||
memory: this.buildParameters.remoteBuildMemory,
|
|
||||||
cpu: this.buildParameters.remoteBuildCpu,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
env: [
|
|
||||||
{
|
|
||||||
name: 'GITHUB_SHA',
|
|
||||||
value: this.buildId,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
job.spec.backoffLimit = 1;
|
|
||||||
return job;
|
|
||||||
}
|
|
||||||
runJob(command, image) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
try {
|
|
||||||
this.setUniqueBuildId();
|
|
||||||
const jobSpec = this.getJobSpec(command, image);
|
|
||||||
core.info('Creating build job');
|
|
||||||
yield this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
|
||||||
core.info('Job created');
|
|
||||||
yield this.watchPersistentVolumeClaimUntilBoundToContainer();
|
|
||||||
core.info('PVC Bound');
|
|
||||||
this.setPodNameAndContainerName(yield this.getPod());
|
|
||||||
core.info('Watching pod and streaming logs');
|
|
||||||
yield this.watchUntilPodRunning();
|
|
||||||
yield this.streamLogs();
|
|
||||||
yield this.cleanup();
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
yield this.cleanup();
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getPod() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
if (this.podName === '') {
|
|
||||||
const pod = (yield this.kubeClient.listNamespacedPod(this.namespace)).body.items.find((x) => { var _a, _b; return ((_b = (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.labels) === null || _b === void 0 ? void 0 : _b['job-name']) === this.jobName; });
|
|
||||||
if (pod === undefined) {
|
|
||||||
throw new Error("pod with job-name label doesn't exist");
|
|
||||||
}
|
|
||||||
return pod;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (yield this.kubeClient.readNamespacedPod(this.podName, this.namespace)).body;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
runCloneJob() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
yield this.runJob([
|
|
||||||
'/bin/ash',
|
|
||||||
'-c',
|
|
||||||
`apk update;
|
|
||||||
apk add git-lfs;
|
|
||||||
ls /credentials/
|
|
||||||
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
|
|
||||||
echo "end"`,
|
|
||||||
], 'alpine/git');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
runBuildJob() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
yield this.runJob([
|
|
||||||
'bin/bash',
|
|
||||||
'-c',
|
|
||||||
`ls
|
|
||||||
for f in ./credentials/*; do export $(basename $f)="$(cat $f)"; done
|
|
||||||
ls /data
|
|
||||||
ls /data/builder
|
|
||||||
ls /data/builder/dist
|
|
||||||
cp -r /data/builder/dist/default-build-script /UnityBuilderAction
|
|
||||||
cp -r /data/builder/dist/entrypoint.sh /entrypoint.sh
|
|
||||||
cp -r /data/builder/dist/steps /steps
|
|
||||||
chmod -R +x /entrypoint.sh
|
|
||||||
chmod -R +x /steps
|
|
||||||
/entrypoint.sh
|
|
||||||
`,
|
|
||||||
], this.baseImage.toString());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
watchPersistentVolumeClaimUntilBoundToContainer() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
yield async_wait_until_1.waitUntil(() => __awaiter(this, void 0, void 0, function* () { return (yield this.getPVCPhase()) !== 'Pending'; }));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getPodStatusPhase() {
|
|
||||||
var _a, _b;
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
return (_b = (_a = (yield this.kubeClient.readNamespacedPod(this.podName, this.namespace))) === null || _a === void 0 ? void 0 : _a.body.status) === null || _b === void 0 ? void 0 : _b.phase;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
watchUntilPodRunning() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
yield async_wait_until_1.waitUntil(() => __awaiter(this, void 0, void 0, function* () {
|
|
||||||
(yield this.getPodStatusPhase()) !== 'Pending';
|
|
||||||
}));
|
|
||||||
const phase = yield this.getPodStatusPhase();
|
|
||||||
if (phase === 'Running') {
|
|
||||||
core.info('Pod no longer pending');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
core.error('Pod failed to reach running phase');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setPodNameAndContainerName(pod) {
|
|
||||||
var _a, _b, _c;
|
|
||||||
this.podName = ((_a = pod.metadata) === null || _a === void 0 ? void 0 : _a.name) || '';
|
|
||||||
this.containerName = ((_c = (_b = pod.status) === null || _b === void 0 ? void 0 : _b.containerStatuses) === null || _c === void 0 ? void 0 : _c[0].name) || '';
|
|
||||||
}
|
|
||||||
streamLogs() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
core.info(`Streaming logs from pod: ${this.podName} container: ${this.containerName} namespace: ${this.namespace}`);
|
|
||||||
const stream = new stream_1.Writable();
|
|
||||||
stream._write = (chunk, encoding, next) => {
|
|
||||||
core.info(chunk.toString());
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
const logOptions = {
|
|
||||||
follow: true,
|
|
||||||
pretty: true,
|
|
||||||
previous: true,
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
yield new Promise((resolve) => new client_node_1.Log(this.kubeConfig).log(this.namespace, this.podName, this.containerName, stream, resolve, logOptions));
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
core.info('end of log stream');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cleanup() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
core.info('cleaning up');
|
|
||||||
yield this.kubeClientBatch.deleteNamespacedJob(this.jobName, this.namespace);
|
|
||||||
yield this.kubeClient.deleteNamespacedPersistentVolumeClaim(this.pvcName, this.namespace);
|
|
||||||
yield this.kubeClient.deleteNamespacedSecret(this.secretName, this.namespace);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.default = Kubernetes;
|
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 85487:
|
/***/ 85487:
|
||||||
|
|
@ -1655,6 +1211,503 @@ class AWSBuildRunner {
|
||||||
exports.default = AWSBuildRunner;
|
exports.default = AWSBuildRunner;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 81730:
|
||||||
|
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
const k8s = __importStar(__webpack_require__(89679));
|
||||||
|
const core = __importStar(__webpack_require__(42186));
|
||||||
|
const client_node_1 = __webpack_require__(89679);
|
||||||
|
const stream_1 = __webpack_require__(92413);
|
||||||
|
const async_wait_until_1 = __webpack_require__(41299);
|
||||||
|
const kubernetes_storage_1 = __importDefault(__webpack_require__(38941));
|
||||||
|
const base64 = __webpack_require__(85848);
|
||||||
|
class Kubernetes {
|
||||||
|
constructor(buildParameters, baseImage) {
|
||||||
|
this.buildId = '';
|
||||||
|
this.pvcName = '';
|
||||||
|
this.secretName = '';
|
||||||
|
this.jobName = '';
|
||||||
|
this.podName = '';
|
||||||
|
this.containerName = '';
|
||||||
|
const kc = new k8s.KubeConfig();
|
||||||
|
kc.loadFromDefault();
|
||||||
|
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
|
||||||
|
const k8sBatchApi = kc.makeApiClient(k8s.BatchV1Api);
|
||||||
|
core.info('Loaded default Kubernetes configuration for this environment');
|
||||||
|
this.kubeConfig = kc;
|
||||||
|
this.kubeClient = k8sApi;
|
||||||
|
this.kubeClientBatch = k8sBatchApi;
|
||||||
|
this.namespace = 'default';
|
||||||
|
this.buildParameters = buildParameters;
|
||||||
|
this.baseImage = baseImage;
|
||||||
|
this.setUniqueBuildId();
|
||||||
|
}
|
||||||
|
setUniqueBuildId() {
|
||||||
|
const buildId = Kubernetes.uuidv4();
|
||||||
|
const pvcName = `unity-builder-pvc-${buildId}`;
|
||||||
|
const secretName = `build-credentials-${buildId}`;
|
||||||
|
const jobName = `unity-builder-job-${buildId}`;
|
||||||
|
this.buildId = buildId;
|
||||||
|
this.pvcName = pvcName;
|
||||||
|
this.secretName = secretName;
|
||||||
|
this.jobName = jobName;
|
||||||
|
}
|
||||||
|
run() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
core.info('Running Remote Builder on Kubernetes');
|
||||||
|
const defaultSecretsArray = [
|
||||||
|
{
|
||||||
|
ParameterKey: 'GithubToken',
|
||||||
|
EnvironmentVariable: 'GITHUB_TOKEN',
|
||||||
|
ParameterValue: this.buildParameters.githubToken,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'UNITY_LICENSE',
|
||||||
|
EnvironmentVariable: 'UNITY_LICENSE',
|
||||||
|
ParameterValue: process.env.UNITY_LICENSE || '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'ANDROID_KEYSTORE_BASE64',
|
||||||
|
EnvironmentVariable: 'ANDROID_KEYSTORE_BASE64',
|
||||||
|
ParameterValue: this.buildParameters.androidKeystoreBase64,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'ANDROID_KEYSTORE_PASS',
|
||||||
|
EnvironmentVariable: 'ANDROID_KEYSTORE_PASS',
|
||||||
|
ParameterValue: this.buildParameters.androidKeystorePass,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ParameterKey: 'ANDROID_KEYALIAS_PASS',
|
||||||
|
EnvironmentVariable: 'ANDROID_KEYALIAS_PASS',
|
||||||
|
ParameterValue: this.buildParameters.androidKeyaliasPass,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
try {
|
||||||
|
// setup
|
||||||
|
yield this.createSecret(defaultSecretsArray);
|
||||||
|
yield kubernetes_storage_1.default.createPersistentVolumeClaim(this.buildParameters, this.pvcName, this.kubeClient, this.namespace);
|
||||||
|
// run
|
||||||
|
yield this.runCloneJob();
|
||||||
|
yield this.runBuildJob();
|
||||||
|
yield this.cleanup();
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
core.error(JSON.stringify(error.response, undefined, 4));
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
core.setOutput('volume', this.pvcName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
createSecret(secrets) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const secret = new k8s.V1Secret();
|
||||||
|
secret.apiVersion = 'v1';
|
||||||
|
secret.kind = 'Secret';
|
||||||
|
secret.type = 'Opaque';
|
||||||
|
secret.metadata = {
|
||||||
|
name: this.secretName,
|
||||||
|
};
|
||||||
|
secret.data = {};
|
||||||
|
for (const buildSecret of secrets) {
|
||||||
|
secret.data[buildSecret.EnvironmentVariable] = base64.encode(buildSecret.ParameterValue);
|
||||||
|
secret.data[`${buildSecret.EnvironmentVariable}_NAME`] = base64.encode(buildSecret.ParameterKey);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
yield this.kubeClient.createNamespacedSecret(this.namespace, secret);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getJobSpec(command, image) {
|
||||||
|
const job = new k8s.V1Job();
|
||||||
|
job.apiVersion = 'batch/v1';
|
||||||
|
job.kind = 'Job';
|
||||||
|
job.metadata = {
|
||||||
|
name: this.jobName,
|
||||||
|
labels: {
|
||||||
|
app: 'unity-builder',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
job.spec = {
|
||||||
|
template: {
|
||||||
|
spec: {
|
||||||
|
volumes: [
|
||||||
|
{
|
||||||
|
name: 'data',
|
||||||
|
persistentVolumeClaim: {
|
||||||
|
claimName: this.pvcName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'credentials',
|
||||||
|
secret: {
|
||||||
|
secretName: this.secretName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
containers: [
|
||||||
|
{
|
||||||
|
name: 'main',
|
||||||
|
image,
|
||||||
|
command,
|
||||||
|
resources: {
|
||||||
|
requests: {
|
||||||
|
memory: this.buildParameters.remoteBuildMemory,
|
||||||
|
cpu: this.buildParameters.remoteBuildCpu,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
env: [
|
||||||
|
{
|
||||||
|
name: 'GITHUB_SHA',
|
||||||
|
value: this.buildId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
job.spec.backoffLimit = 1;
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
runJob(command, image) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
try {
|
||||||
|
this.setUniqueBuildId();
|
||||||
|
const jobSpec = this.getJobSpec(command, image);
|
||||||
|
core.info('Creating build job');
|
||||||
|
yield this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
||||||
|
core.info('Job created');
|
||||||
|
yield kubernetes_storage_1.default.watchPersistentVolumeClaimUntilBoundToContainer(this.kubeClient, this.pvcName, this.namespace);
|
||||||
|
core.info('PVC Bound');
|
||||||
|
this.setPodNameAndContainerName(yield this.getPod());
|
||||||
|
core.info('Watching pod and streaming logs');
|
||||||
|
yield this.watchUntilPodRunning();
|
||||||
|
yield this.streamLogs();
|
||||||
|
yield this.cleanup();
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
yield this.cleanup();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getPod() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
if (this.podName === '') {
|
||||||
|
const pod = (yield this.kubeClient.listNamespacedPod(this.namespace)).body.items.find((x) => { var _a, _b; return ((_b = (_a = x.metadata) === null || _a === void 0 ? void 0 : _a.labels) === null || _b === void 0 ? void 0 : _b['job-name']) === this.jobName; });
|
||||||
|
if (pod === undefined) {
|
||||||
|
throw new Error("pod with job-name label doesn't exist");
|
||||||
|
}
|
||||||
|
return pod;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (yield this.kubeClient.readNamespacedPod(this.podName, this.namespace)).body;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
runCloneJob() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
yield this.runJob([
|
||||||
|
'/bin/ash',
|
||||||
|
'-c',
|
||||||
|
`apk update;
|
||||||
|
apk add git-lfs;
|
||||||
|
ls /credentials/
|
||||||
|
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
|
||||||
|
echo "end"`,
|
||||||
|
], 'alpine/git');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
runBuildJob() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
yield this.runJob([
|
||||||
|
'bin/bash',
|
||||||
|
'-c',
|
||||||
|
`ls
|
||||||
|
for f in ./credentials/*; do export $(basename $f)="$(cat $f)"; done
|
||||||
|
ls /data
|
||||||
|
ls /data/builder
|
||||||
|
ls /data/builder/dist
|
||||||
|
cp -r /data/builder/dist/default-build-script /UnityBuilderAction
|
||||||
|
cp -r /data/builder/dist/entrypoint.sh /entrypoint.sh
|
||||||
|
cp -r /data/builder/dist/steps /steps
|
||||||
|
chmod -R +x /entrypoint.sh
|
||||||
|
chmod -R +x /steps
|
||||||
|
/entrypoint.sh
|
||||||
|
`,
|
||||||
|
], this.baseImage.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
getPodStatusPhase() {
|
||||||
|
var _a, _b;
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
return (_b = (_a = (yield this.kubeClient.readNamespacedPod(this.podName, this.namespace))) === null || _a === void 0 ? void 0 : _a.body.status) === null || _b === void 0 ? void 0 : _b.phase;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
watchUntilPodRunning() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
yield async_wait_until_1.waitUntil(() => __awaiter(this, void 0, void 0, function* () {
|
||||||
|
(yield this.getPodStatusPhase()) !== 'Pending';
|
||||||
|
}));
|
||||||
|
const phase = yield this.getPodStatusPhase();
|
||||||
|
if (phase === 'Running') {
|
||||||
|
core.info('Pod no longer pending');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.error('Pod failed to reach running phase');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setPodNameAndContainerName(pod) {
|
||||||
|
var _a, _b, _c;
|
||||||
|
this.podName = ((_a = pod.metadata) === null || _a === void 0 ? void 0 : _a.name) || '';
|
||||||
|
this.containerName = ((_c = (_b = pod.status) === null || _b === void 0 ? void 0 : _b.containerStatuses) === null || _c === void 0 ? void 0 : _c[0].name) || '';
|
||||||
|
}
|
||||||
|
streamLogs() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
core.info(`Streaming logs from pod: ${this.podName} container: ${this.containerName} namespace: ${this.namespace}`);
|
||||||
|
const stream = new stream_1.Writable();
|
||||||
|
stream._write = (chunk, encoding, next) => {
|
||||||
|
core.info(chunk.toString());
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
const logOptions = {
|
||||||
|
follow: true,
|
||||||
|
pretty: true,
|
||||||
|
previous: true,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
yield new Promise((resolve) => new client_node_1.Log(this.kubeConfig).log(this.namespace, this.podName, this.containerName, stream, resolve, logOptions));
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
core.info('end of log stream');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
cleanup() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
core.info('cleaning up');
|
||||||
|
yield this.kubeClientBatch.deleteNamespacedJob(this.jobName, this.namespace);
|
||||||
|
yield this.kubeClient.deleteNamespacedPersistentVolumeClaim(this.pvcName, this.namespace);
|
||||||
|
yield this.kubeClient.deleteNamespacedSecret(this.secretName, this.namespace);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.default = Kubernetes;
|
||||||
|
|
||||||
|
|
||||||
|
/***/ }),
|
||||||
|
|
||||||
|
/***/ 38941:
|
||||||
|
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||||
|
const async_wait_until_1 = __importDefault(__webpack_require__(41299));
|
||||||
|
const core = __importStar(__webpack_require__(42186));
|
||||||
|
const k8s = __importStar(__webpack_require__(89679));
|
||||||
|
class KubernetesStorage {
|
||||||
|
static getPVCPhase(kubeClient, name, namespace) {
|
||||||
|
var _a;
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
return (_a = (yield kubeClient.readNamespacedPersistentVolumeClaimStatus(name, namespace)).body.status) === null || _a === void 0 ? void 0 : _a.phase;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static watchPersistentVolumeClaimUntilBoundToContainer(kubeClient, name, namespace) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
yield async_wait_until_1.default(() => __awaiter(this, void 0, void 0, function* () { return (yield this.getPVCPhase(kubeClient, name, namespace)) !== 'Pending'; }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static createPersistentVolumeClaim(buildParameters, pvcName, kubeClient, namespace) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
if (buildParameters.kubeVolume) {
|
||||||
|
core.info(buildParameters.kubeVolume);
|
||||||
|
pvcName = buildParameters.kubeVolume;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pvc = new k8s.V1PersistentVolumeClaim();
|
||||||
|
pvc.apiVersion = 'v1';
|
||||||
|
pvc.kind = 'PersistentVolumeClaim';
|
||||||
|
pvc.metadata = {
|
||||||
|
name: pvcName,
|
||||||
|
};
|
||||||
|
pvc.spec = {
|
||||||
|
accessModes: ['ReadWriteOnce'],
|
||||||
|
volumeMode: 'Filesystem',
|
||||||
|
resources: {
|
||||||
|
requests: {
|
||||||
|
storage: buildParameters.kubeVolumeSize,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
yield kubeClient.createNamespacedPersistentVolumeClaim(namespace, pvc);
|
||||||
|
core.info(`Persistent Volume created, ${yield KubernetesStorage.getPVCPhase(kubeClient, pvcName, namespace)}`);
|
||||||
|
yield this.watchPersistentVolumeClaimUntilBoundToContainer(kubeClient, pvcName, pvcName);
|
||||||
|
core.info(JSON.stringify((yield kubeClient.readNamespacedPersistentVolumeClaimStatus(pvcName, namespace)).body, undefined, 4));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.default = KubernetesStorage;
|
||||||
|
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
/***/ 92560:
|
/***/ 92560:
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -9,7 +9,7 @@ import Platform from './platform';
|
||||||
import Project from './project';
|
import Project from './project';
|
||||||
import Unity from './unity';
|
import Unity from './unity';
|
||||||
import Versioning from './versioning';
|
import Versioning from './versioning';
|
||||||
import Kubernetes from './kubernetes';
|
import Kubernetes from './remote-builder/kubernetes-build-platform';
|
||||||
import RemoteBuilder from './remote-builder/remote-builder';
|
import RemoteBuilder from './remote-builder/remote-builder';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import * as k8s from '@kubernetes/client-node';
|
import * as k8s from '@kubernetes/client-node';
|
||||||
import { BuildParameters } from '.';
|
import { BuildParameters } from '..';
|
||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import { KubeConfig, Log } from '@kubernetes/client-node';
|
import { KubeConfig, Log } from '@kubernetes/client-node';
|
||||||
import { Writable } from 'stream';
|
import { Writable } from 'stream';
|
||||||
import { RemoteBuilderProviderInterface } from './remote-builder/remote-builder-provider-interface';
|
import { RemoteBuilderProviderInterface } from './remote-builder-provider-interface';
|
||||||
import RemoteBuilderSecret from './remote-builder/remote-builder-secret';
|
import RemoteBuilderSecret from './remote-builder-secret';
|
||||||
import { waitUntil } from 'async-wait-until';
|
import { waitUntil } from 'async-wait-until';
|
||||||
|
import KubernetesStorage from './kubernetes-storage';
|
||||||
|
|
||||||
const base64 = require('base-64');
|
const base64 = require('base-64');
|
||||||
|
|
||||||
|
|
@ -85,7 +86,13 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
try {
|
try {
|
||||||
// setup
|
// setup
|
||||||
await this.createSecret(defaultSecretsArray);
|
await this.createSecret(defaultSecretsArray);
|
||||||
await this.createPersistentVolumeClaim();
|
await KubernetesStorage.createPersistentVolumeClaim(
|
||||||
|
this.buildParameters,
|
||||||
|
this.pvcName,
|
||||||
|
this.kubeClient,
|
||||||
|
this.namespace,
|
||||||
|
);
|
||||||
|
|
||||||
// run
|
// run
|
||||||
await this.runCloneJob();
|
await this.runCloneJob();
|
||||||
await this.runBuildJob();
|
await this.runBuildJob();
|
||||||
|
|
@ -119,36 +126,6 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPVCPhase() {
|
|
||||||
return (await this.kubeClient.readNamespacedPersistentVolumeClaimStatus(this.pvcName, this.namespace)).body.status
|
|
||||||
?.phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
async createPersistentVolumeClaim() {
|
|
||||||
if (this.buildParameters.kubeVolume) {
|
|
||||||
core.info(this.buildParameters.kubeVolume);
|
|
||||||
this.pvcName = this.buildParameters.kubeVolume;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const pvc = new k8s.V1PersistentVolumeClaim();
|
|
||||||
pvc.apiVersion = 'v1';
|
|
||||||
pvc.kind = 'PersistentVolumeClaim';
|
|
||||||
pvc.metadata = {
|
|
||||||
name: this.pvcName,
|
|
||||||
};
|
|
||||||
pvc.spec = {
|
|
||||||
accessModes: ['ReadWriteOnce'],
|
|
||||||
volumeMode: 'Filesystem',
|
|
||||||
resources: {
|
|
||||||
requests: {
|
|
||||||
storage: this.buildParameters.kubeVolumeSize,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
await this.kubeClient.createNamespacedPersistentVolumeClaim(this.namespace, pvc);
|
|
||||||
core.info(`Persistent Volume created, ${await this.getPVCPhase()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
getJobSpec(command: string[], image: string) {
|
getJobSpec(command: string[], image: string) {
|
||||||
const job = new k8s.V1Job();
|
const job = new k8s.V1Job();
|
||||||
job.apiVersion = 'batch/v1';
|
job.apiVersion = 'batch/v1';
|
||||||
|
|
@ -282,7 +259,11 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
core.info('Creating build job');
|
core.info('Creating build job');
|
||||||
await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
||||||
core.info('Job created');
|
core.info('Job created');
|
||||||
await this.watchPersistentVolumeClaimUntilBoundToContainer();
|
await KubernetesStorage.watchPersistentVolumeClaimUntilBoundToContainer(
|
||||||
|
this.kubeClient,
|
||||||
|
this.pvcName,
|
||||||
|
this.namespace,
|
||||||
|
);
|
||||||
core.info('PVC Bound');
|
core.info('PVC Bound');
|
||||||
this.setPodNameAndContainerName(await this.getPod());
|
this.setPodNameAndContainerName(await this.getPod());
|
||||||
core.info('Watching pod and streaming logs');
|
core.info('Watching pod and streaming logs');
|
||||||
|
|
@ -352,10 +333,6 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchPersistentVolumeClaimUntilBoundToContainer() {
|
|
||||||
await waitUntil(async () => (await this.getPVCPhase()) !== 'Pending');
|
|
||||||
}
|
|
||||||
|
|
||||||
async getPodStatusPhase() {
|
async getPodStatusPhase() {
|
||||||
return (await this.kubeClient.readNamespacedPod(this.podName, this.namespace))?.body.status?.phase;
|
return (await this.kubeClient.readNamespacedPod(this.podName, this.namespace))?.body.status?.phase;
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import waitUntil from 'async-wait-until';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as k8s from '@kubernetes/client-node';
|
||||||
|
|
||||||
|
class KubernetesStorage {
|
||||||
|
public static async getPVCPhase(kubeClient, name, namespace) {
|
||||||
|
return (await kubeClient.readNamespacedPersistentVolumeClaimStatus(name, namespace)).body.status?.phase;
|
||||||
|
}
|
||||||
|
public static async watchPersistentVolumeClaimUntilBoundToContainer(kubeClient, name, namespace) {
|
||||||
|
await waitUntil(async () => (await this.getPVCPhase(kubeClient, name, namespace)) !== 'Pending');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async createPersistentVolumeClaim(buildParameters, pvcName, kubeClient, namespace) {
|
||||||
|
if (buildParameters.kubeVolume) {
|
||||||
|
core.info(buildParameters.kubeVolume);
|
||||||
|
pvcName = buildParameters.kubeVolume;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pvc = new k8s.V1PersistentVolumeClaim();
|
||||||
|
pvc.apiVersion = 'v1';
|
||||||
|
pvc.kind = 'PersistentVolumeClaim';
|
||||||
|
pvc.metadata = {
|
||||||
|
name: pvcName,
|
||||||
|
};
|
||||||
|
pvc.spec = {
|
||||||
|
accessModes: ['ReadWriteOnce'],
|
||||||
|
volumeMode: 'Filesystem',
|
||||||
|
resources: {
|
||||||
|
requests: {
|
||||||
|
storage: buildParameters.kubeVolumeSize,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await kubeClient.createNamespacedPersistentVolumeClaim(namespace, pvc);
|
||||||
|
core.info(`Persistent Volume created, ${await KubernetesStorage.getPVCPhase(kubeClient, pvcName, namespace)}`);
|
||||||
|
await this.watchPersistentVolumeClaimUntilBoundToContainer(kubeClient, pvcName, pvcName);
|
||||||
|
core.info(
|
||||||
|
JSON.stringify(
|
||||||
|
(await kubeClient.readNamespacedPersistentVolumeClaimStatus(pvcName, namespace)).body,
|
||||||
|
undefined,
|
||||||
|
4,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default KubernetesStorage;
|
||||||
Loading…
Reference in New Issue