Fix: error handling
parent
4208953d5a
commit
d0402e14b1
|
|
@ -737,6 +737,10 @@ const base64 = __webpack_require__(85848);
|
||||||
const pollInterval = 20000;
|
const pollInterval = 20000;
|
||||||
class Kubernetes {
|
class Kubernetes {
|
||||||
constructor(buildParameters, baseImage) {
|
constructor(buildParameters, baseImage) {
|
||||||
|
this.buildId = '';
|
||||||
|
this.pvcName = '';
|
||||||
|
this.secretName = '';
|
||||||
|
this.jobName = '';
|
||||||
this.podName = '';
|
this.podName = '';
|
||||||
this.containerName = '';
|
this.containerName = '';
|
||||||
const kc = new k8s.KubeConfig();
|
const kc = new k8s.KubeConfig();
|
||||||
|
|
@ -744,21 +748,23 @@ class Kubernetes {
|
||||||
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
|
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
|
||||||
const k8sBatchApi = kc.makeApiClient(k8s.BatchV1Api);
|
const k8sBatchApi = kc.makeApiClient(k8s.BatchV1Api);
|
||||||
core.info('Loaded default Kubernetes configuration for this environment');
|
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 buildId = Kubernetes.uuidv4();
|
||||||
const pvcName = `unity-builder-pvc-${buildId}`;
|
const pvcName = `unity-builder-pvc-${buildId}`;
|
||||||
const secretName = `build-credentials-${buildId}`;
|
const secretName = `build-credentials-${buildId}`;
|
||||||
const jobName = `unity-builder-job-${buildId}`;
|
const jobName = `unity-builder-job-${buildId}`;
|
||||||
const namespace = 'default';
|
|
||||||
this.kubeConfig = kc;
|
|
||||||
this.kubeClient = k8sApi;
|
|
||||||
this.kubeClientBatch = k8sBatchApi;
|
|
||||||
this.buildId = buildId;
|
this.buildId = buildId;
|
||||||
this.pvcName = pvcName;
|
this.pvcName = pvcName;
|
||||||
this.secretName = secretName;
|
this.secretName = secretName;
|
||||||
this.jobName = jobName;
|
this.jobName = jobName;
|
||||||
this.namespace = namespace;
|
|
||||||
this.buildParameters = buildParameters;
|
|
||||||
this.baseImage = baseImage;
|
|
||||||
}
|
}
|
||||||
run() {
|
run() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
|
@ -799,7 +805,7 @@ class Kubernetes {
|
||||||
yield this.runBuildJob();
|
yield this.runBuildJob();
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
core.error(JSON.stringify(error, undefined, 4));
|
core.error(JSON.stringify(error.response.body, undefined, 4));
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
core.setOutput('volume', this.pvcName);
|
core.setOutput('volume', this.pvcName);
|
||||||
|
|
@ -978,21 +984,21 @@ class Kubernetes {
|
||||||
job.spec.backoffLimit = 1;
|
job.spec.backoffLimit = 1;
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
runJob(jobSpec) {
|
runJob(command, image) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
|
this.setUniqueBuildId();
|
||||||
|
const jobSpec = this.getJobSpec(command, image);
|
||||||
core.info('Creating build job');
|
core.info('Creating build job');
|
||||||
yield this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
yield this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
|
||||||
core.info('Job created');
|
core.info('Job created');
|
||||||
// We watch the PVC first to allow some time for K8s to notice the job we created and setup a pod.
|
yield this.watchPersistentVolumeClaimUntilBoundToContainer();
|
||||||
yield this.watchPersistentVolumeClaimUntilReady();
|
|
||||||
// TODO: Wait for something more reliable so we don't potentially get the pod before k8s has created it based on the job.
|
|
||||||
this.setPodNameAndContainerName(yield this.getPod());
|
this.setPodNameAndContainerName(yield this.getPod());
|
||||||
yield this.watchUntilPodRunning();
|
yield this.watchUntilPodRunning();
|
||||||
yield this.streamLogs();
|
yield this.streamLogs();
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
core.error(JSON.stringify(error.response.body, undefined, 4));
|
throw error;
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
yield this.cleanup();
|
yield this.cleanup();
|
||||||
|
|
@ -1003,6 +1009,9 @@ class Kubernetes {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
if (this.podName === '') {
|
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; });
|
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;
|
return pod;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -1012,7 +1021,7 @@ class Kubernetes {
|
||||||
}
|
}
|
||||||
runCloneJob() {
|
runCloneJob() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
yield this.runJob(this.getJobSpec([
|
yield this.runJob([
|
||||||
'/bin/ash',
|
'/bin/ash',
|
||||||
'-c',
|
'-c',
|
||||||
`apk update;
|
`apk update;
|
||||||
|
|
@ -1026,12 +1035,12 @@ class Kubernetes {
|
||||||
git checkout $GITHUB_SHA;
|
git checkout $GITHUB_SHA;
|
||||||
ls
|
ls
|
||||||
echo "end"`,
|
echo "end"`,
|
||||||
], 'alpine/git'));
|
], 'alpine/git');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
runBuildJob() {
|
runBuildJob() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
yield this.runJob(this.getJobSpec([
|
yield this.runJob([
|
||||||
'bin/bash',
|
'bin/bash',
|
||||||
'-c',
|
'-c',
|
||||||
`ls
|
`ls
|
||||||
|
|
@ -1046,16 +1055,16 @@ class Kubernetes {
|
||||||
chmod -R +x /steps
|
chmod -R +x /steps
|
||||||
/entrypoint.sh
|
/entrypoint.sh
|
||||||
`,
|
`,
|
||||||
], this.baseImage.toString()));
|
], this.baseImage.toString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
watchPersistentVolumeClaimUntilReady() {
|
watchPersistentVolumeClaimUntilBoundToContainer() {
|
||||||
var _a;
|
var _a;
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
yield new Promise((resolve) => setTimeout(resolve, pollInterval));
|
yield new Promise((resolve) => setTimeout(resolve, pollInterval));
|
||||||
const queryResult = yield this.kubeClient.readNamespacedPersistentVolumeClaim(this.pvcName, this.namespace);
|
const queryResult = yield this.kubeClient.readNamespacedPersistentVolumeClaim(this.pvcName, this.namespace);
|
||||||
if (((_a = queryResult.body.status) === null || _a === void 0 ? void 0 : _a.phase) === 'Pending') {
|
if (((_a = queryResult.body.status) === null || _a === void 0 ? void 0 : _a.phase) === 'Pending') {
|
||||||
yield this.watchPersistentVolumeClaimUntilReady();
|
yield this.watchPersistentVolumeClaimUntilBoundToContainer();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.info('Persistent Volume ready for claims');
|
core.info('Persistent Volume ready for claims');
|
||||||
|
|
@ -1086,8 +1095,8 @@ class Kubernetes {
|
||||||
}
|
}
|
||||||
setPodNameAndContainerName(pod) {
|
setPodNameAndContainerName(pod) {
|
||||||
var _a, _b, _c;
|
var _a, _b, _c;
|
||||||
this.podName = ((_a = pod === null || pod === void 0 ? void 0 : pod.metadata) === null || _a === void 0 ? void 0 : _a.name) || '';
|
this.podName = ((_a = pod.metadata) === null || _a === void 0 ? void 0 : _a.name) || '';
|
||||||
this.containerName = ((_c = (_b = pod === null || pod === void 0 ? void 0 : pod.status) === null || _b === void 0 ? void 0 : _b.containerStatuses) === null || _c === void 0 ? void 0 : _c[0].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() {
|
streamLogs() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -13,12 +13,12 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
private kubeConfig: KubeConfig;
|
private kubeConfig: KubeConfig;
|
||||||
private kubeClient: k8s.CoreV1Api;
|
private kubeClient: k8s.CoreV1Api;
|
||||||
private kubeClientBatch: k8s.BatchV1Api;
|
private kubeClientBatch: k8s.BatchV1Api;
|
||||||
private buildId: string;
|
private buildId: string = '';
|
||||||
private buildParameters: BuildParameters;
|
private buildParameters: BuildParameters;
|
||||||
private baseImage: any;
|
private baseImage: any;
|
||||||
private pvcName: string;
|
private pvcName: string = '';
|
||||||
private secretName: string;
|
private secretName: string = '';
|
||||||
private jobName: string;
|
private jobName: string = '';
|
||||||
private namespace: string;
|
private namespace: string;
|
||||||
private podName: string = '';
|
private podName: string = '';
|
||||||
private containerName: string = '';
|
private containerName: string = '';
|
||||||
|
|
@ -30,22 +30,27 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
const k8sBatchApi = kc.makeApiClient(k8s.BatchV1Api);
|
const k8sBatchApi = kc.makeApiClient(k8s.BatchV1Api);
|
||||||
core.info('Loaded default Kubernetes configuration for this environment');
|
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 buildId = Kubernetes.uuidv4();
|
||||||
const pvcName = `unity-builder-pvc-${buildId}`;
|
const pvcName = `unity-builder-pvc-${buildId}`;
|
||||||
const secretName = `build-credentials-${buildId}`;
|
const secretName = `build-credentials-${buildId}`;
|
||||||
const jobName = `unity-builder-job-${buildId}`;
|
const jobName = `unity-builder-job-${buildId}`;
|
||||||
const namespace = 'default';
|
|
||||||
|
|
||||||
this.kubeConfig = kc;
|
|
||||||
this.kubeClient = k8sApi;
|
|
||||||
this.kubeClientBatch = k8sBatchApi;
|
|
||||||
this.buildId = buildId;
|
this.buildId = buildId;
|
||||||
this.pvcName = pvcName;
|
this.pvcName = pvcName;
|
||||||
this.secretName = secretName;
|
this.secretName = secretName;
|
||||||
this.jobName = jobName;
|
this.jobName = jobName;
|
||||||
this.namespace = namespace;
|
|
||||||
this.buildParameters = buildParameters;
|
|
||||||
this.baseImage = baseImage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
|
|
@ -85,7 +90,7 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
await this.runCloneJob();
|
await this.runCloneJob();
|
||||||
await this.runBuildJob();
|
await this.runBuildJob();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.error(JSON.stringify(error, undefined, 4));
|
core.error(JSON.stringify(error.response.body, undefined, 4));
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,21 +268,19 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
async runJob(jobSpec: k8s.V1Job) {
|
async runJob(command: string[], image: string) {
|
||||||
try {
|
try {
|
||||||
|
this.setUniqueBuildId();
|
||||||
|
const jobSpec = this.getJobSpec(command, image);
|
||||||
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');
|
||||||
// We watch the PVC first to allow some time for K8s to notice the job we created and setup a pod.
|
await this.watchPersistentVolumeClaimUntilBoundToContainer();
|
||||||
await this.watchPersistentVolumeClaimUntilReady();
|
|
||||||
|
|
||||||
// TODO: Wait for something more reliable so we don't potentially get the pod before k8s has created it based on the job.
|
|
||||||
this.setPodNameAndContainerName(await this.getPod());
|
this.setPodNameAndContainerName(await this.getPod());
|
||||||
|
|
||||||
await this.watchUntilPodRunning();
|
await this.watchUntilPodRunning();
|
||||||
await this.streamLogs();
|
await this.streamLogs();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.error(JSON.stringify(error.response.body, undefined, 4));
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
await this.cleanup();
|
await this.cleanup();
|
||||||
}
|
}
|
||||||
|
|
@ -288,6 +291,9 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
const pod = (await this.kubeClient.listNamespacedPod(this.namespace)).body.items.find(
|
const pod = (await this.kubeClient.listNamespacedPod(this.namespace)).body.items.find(
|
||||||
(x) => x.metadata?.labels?.['job-name'] === this.jobName,
|
(x) => x.metadata?.labels?.['job-name'] === this.jobName,
|
||||||
);
|
);
|
||||||
|
if (pod === undefined) {
|
||||||
|
throw new Error("pod with job-name label doesn't exist");
|
||||||
|
}
|
||||||
return pod;
|
return pod;
|
||||||
} else {
|
} else {
|
||||||
return (await this.kubeClient.readNamespacedPod(this.podName, this.namespace)).body;
|
return (await this.kubeClient.readNamespacedPod(this.podName, this.namespace)).body;
|
||||||
|
|
@ -296,11 +302,10 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
|
|
||||||
async runCloneJob() {
|
async runCloneJob() {
|
||||||
await this.runJob(
|
await this.runJob(
|
||||||
this.getJobSpec(
|
[
|
||||||
[
|
'/bin/ash',
|
||||||
'/bin/ash',
|
'-c',
|
||||||
'-c',
|
`apk update;
|
||||||
`apk update;
|
|
||||||
apk add git-lfs;
|
apk add git-lfs;
|
||||||
ls /credentials/
|
ls /credentials/
|
||||||
export GITHUB_TOKEN=$(cat /credentials/GITHUB_TOKEN);
|
export GITHUB_TOKEN=$(cat /credentials/GITHUB_TOKEN);
|
||||||
|
|
@ -311,19 +316,17 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
git checkout $GITHUB_SHA;
|
git checkout $GITHUB_SHA;
|
||||||
ls
|
ls
|
||||||
echo "end"`,
|
echo "end"`,
|
||||||
],
|
],
|
||||||
'alpine/git',
|
'alpine/git',
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async runBuildJob() {
|
async runBuildJob() {
|
||||||
await this.runJob(
|
await this.runJob(
|
||||||
this.getJobSpec(
|
[
|
||||||
[
|
'bin/bash',
|
||||||
'bin/bash',
|
'-c',
|
||||||
'-c',
|
`ls
|
||||||
`ls
|
|
||||||
for f in ./credentials/*; do export $(basename $f)="$(cat $f)"; done
|
for f in ./credentials/*; do export $(basename $f)="$(cat $f)"; done
|
||||||
ls /data
|
ls /data
|
||||||
ls /data/builder
|
ls /data/builder
|
||||||
|
|
@ -335,18 +338,17 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
chmod -R +x /steps
|
chmod -R +x /steps
|
||||||
/entrypoint.sh
|
/entrypoint.sh
|
||||||
`,
|
`,
|
||||||
],
|
],
|
||||||
this.baseImage.toString(),
|
this.baseImage.toString(),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchPersistentVolumeClaimUntilReady() {
|
async watchPersistentVolumeClaimUntilBoundToContainer() {
|
||||||
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
||||||
const queryResult = await this.kubeClient.readNamespacedPersistentVolumeClaim(this.pvcName, this.namespace);
|
const queryResult = await this.kubeClient.readNamespacedPersistentVolumeClaim(this.pvcName, this.namespace);
|
||||||
|
|
||||||
if (queryResult.body.status?.phase === 'Pending') {
|
if (queryResult.body.status?.phase === 'Pending') {
|
||||||
await this.watchPersistentVolumeClaimUntilReady();
|
await this.watchPersistentVolumeClaimUntilBoundToContainer();
|
||||||
} else {
|
} else {
|
||||||
core.info('Persistent Volume ready for claims');
|
core.info('Persistent Volume ready for claims');
|
||||||
}
|
}
|
||||||
|
|
@ -373,9 +375,9 @@ class Kubernetes implements RemoteBuilderProviderInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPodNameAndContainerName(pod: k8s.V1Pod | any) {
|
setPodNameAndContainerName(pod: k8s.V1Pod) {
|
||||||
this.podName = pod?.metadata?.name || '';
|
this.podName = pod.metadata?.name || '';
|
||||||
this.containerName = pod?.status?.containerStatuses?.[0].name || '';
|
this.containerName = pod.status?.containerStatuses?.[0].name || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async streamLogs() {
|
async streamLogs() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue