cleanup k8s

pull/310/head
Frostebite 2021-12-29 17:25:38 +00:00
parent 81ca1ad876
commit d988a725e4
6 changed files with 126 additions and 163 deletions

206
dist/index.js vendored
View File

@ -1728,9 +1728,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
const k8s = __importStar(__webpack_require__(89679));
const core = __importStar(__webpack_require__(42186));
const kubernetes_storage_1 = __importDefault(__webpack_require__(43951));
const kubernetes_logging_1 = __importDefault(__webpack_require__(68547));
const kubernetes_task_runner_1 = __importDefault(__webpack_require__(7181));
const kubernetes_secret_1 = __importDefault(__webpack_require__(71586));
const kubernetes_utils_1 = __importDefault(__webpack_require__(96494));
const async_wait_until_1 = __importDefault(__webpack_require__(41299));
const kubernetes_job_spec_factory_1 = __importDefault(__webpack_require__(1739));
const kubernetes_service_account_1 = __importDefault(__webpack_require__(42915));
@ -1786,11 +1785,11 @@ class Kubernetes {
cloud_runner_logger_1.default.log('Creating build job');
yield this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
cloud_runner_logger_1.default.log('Job created');
this.setPodNameAndContainerName(yield kubernetes_utils_1.default.findPodFromJob(this.kubeClient, this.jobName, this.namespace));
this.setPodNameAndContainerName(yield Kubernetes.findPodFromJob(this.kubeClient, this.jobName, this.namespace));
cloud_runner_logger_1.default.log('Watching pod until running');
yield kubernetes_utils_1.default.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
yield kubernetes_task_runner_1.default.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
cloud_runner_logger_1.default.log('Pod running, streaming logs');
yield kubernetes_logging_1.default.streamLogs(this.kubeConfig, this.kubeClient, this.jobName, this.podName, 'main', this.namespace, cloud_runner_logger_1.default.log);
yield kubernetes_task_runner_1.default.runTask(this.kubeConfig, this.kubeClient, this.jobName, this.podName, 'main', this.namespace, cloud_runner_logger_1.default.log);
yield this.cleanupTaskResources();
}
catch (error) {
@ -1843,6 +1842,16 @@ class Kubernetes {
yield this.kubeClient.deleteNamespacedPersistentVolumeClaim(this.pvcName, this.namespace);
});
}
static findPodFromJob(kubeClient, jobName, namespace) {
return __awaiter(this, void 0, void 0, function* () {
const namespacedPods = yield kubeClient.listNamespacedPod(namespace);
const pod = namespacedPods.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']) === jobName; });
if (pod === undefined) {
throw new Error("pod with job-name label doesn't exist");
}
return pod;
});
}
}
exports.default = Kubernetes;
@ -1996,106 +2005,6 @@ class KubernetesJobSpecFactory {
exports.default = KubernetesJobSpecFactory;
/***/ }),
/***/ 68547:
/***/ (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 client_node_1 = __webpack_require__(89679);
const stream_1 = __webpack_require__(92413);
const cloud_runner_logger_1 = __importDefault(__webpack_require__(22855));
const cloud_runner_state_1 = __webpack_require__(70912);
const core = __importStar(__webpack_require__(42186));
const fs_1 = __importDefault(__webpack_require__(35747));
const cloud_runner_statics_1 = __webpack_require__(90828);
class KubernetesLogging {
static streamLogs(kubeConfig, kubeClient, jobName, podName, containerName, namespace, logCallback) {
return __awaiter(this, void 0, void 0, function* () {
cloud_runner_logger_1.default.log(`Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace}`);
const stream = new stream_1.Writable();
let didStreamAnyLogs = false;
stream._write = (chunk, encoding, next) => {
didStreamAnyLogs = true;
let message = chunk.toString();
message = `[${cloud_runner_statics_1.CloudRunnerStatics.logPrefix}] ${message}`;
if (cloud_runner_state_1.CloudRunnerState.buildParams.logToFile) {
fs_1.default.appendFileSync(`${cloud_runner_state_1.CloudRunnerState.buildGuid}-outputfile.txt`, `${message}\n`);
}
logCallback(message);
next();
};
const logOptions = {
follow: true,
pretty: false,
previous: false,
};
try {
const resultError = yield new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { return new client_node_1.Log(kubeConfig).log(namespace, podName, containerName, stream, resolve, logOptions); }));
if (resultError) {
throw resultError;
}
if (!didStreamAnyLogs) {
core.error('Failed to stream any logs, listing namespace events, check for an error with the container');
core.error(JSON.stringify({
events: (yield kubeClient.listNamespacedEvent(namespace)).body.items
.filter((x) => {
return x.involvedObject.name === podName || x.involvedObject.name === jobName;
})
.map((x) => {
return {
type: x.involvedObject.kind,
name: x.involvedObject.name,
message: x.message,
};
}),
}, undefined, 4));
throw new Error(`No logs streamed from k8s`);
}
}
catch (error) {
throw error;
}
cloud_runner_logger_1.default.log('end of log stream');
});
}
}
exports.default = KubernetesLogging;
/***/ }),
/***/ 71586:
@ -2354,11 +2263,30 @@ exports.default = KubernetesStorage;
/***/ }),
/***/ 96494:
/***/ 7181:
/***/ (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) {
@ -2372,17 +2300,62 @@ 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 client_node_1 = __webpack_require__(89679);
const stream_1 = __webpack_require__(92413);
const cloud_runner_logger_1 = __importDefault(__webpack_require__(22855));
class KubernetesUtilities {
static findPodFromJob(kubeClient, jobName, namespace) {
const cloud_runner_state_1 = __webpack_require__(70912);
const core = __importStar(__webpack_require__(42186));
const fs_1 = __importDefault(__webpack_require__(35747));
const cloud_runner_statics_1 = __webpack_require__(90828);
const async_wait_until_1 = __importDefault(__webpack_require__(41299));
class KubernetesTaskRunner {
static runTask(kubeConfig, kubeClient, jobName, podName, containerName, namespace, logCallback) {
return __awaiter(this, void 0, void 0, function* () {
const namespacedPods = yield kubeClient.listNamespacedPod(namespace);
const pod = namespacedPods.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']) === jobName; });
if (pod === undefined) {
throw new Error("pod with job-name label doesn't exist");
cloud_runner_logger_1.default.log(`Streaming logs from pod: ${podName} container: ${containerName} namespace: ${namespace}`);
const stream = new stream_1.Writable();
let didStreamAnyLogs = false;
stream._write = (chunk, encoding, next) => {
didStreamAnyLogs = true;
let message = chunk.toString();
message = `[${cloud_runner_statics_1.CloudRunnerStatics.logPrefix}] ${message}`;
if (cloud_runner_state_1.CloudRunnerState.buildParams.logToFile) {
fs_1.default.appendFileSync(`${cloud_runner_state_1.CloudRunnerState.buildGuid}-outputfile.txt`, `${message}\n`);
}
logCallback(message);
next();
};
const logOptions = {
follow: true,
pretty: false,
previous: false,
};
try {
const resultError = yield new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { return new client_node_1.Log(kubeConfig).log(namespace, podName, containerName, stream, resolve, logOptions); }));
if (resultError) {
throw resultError;
}
if (!didStreamAnyLogs) {
core.error('Failed to stream any logs, listing namespace events, check for an error with the container');
core.error(JSON.stringify({
events: (yield kubeClient.listNamespacedEvent(namespace)).body.items
.filter((x) => {
return x.involvedObject.name === podName || x.involvedObject.name === jobName;
})
.map((x) => {
return {
type: x.involvedObject.kind,
name: x.involvedObject.name,
message: x.message,
};
}),
}, undefined, 4));
throw new Error(`No logs streamed from k8s`);
}
}
return pod;
catch (error) {
throw error;
}
cloud_runner_logger_1.default.log('end of log stream');
});
}
static watchUntilPodRunning(kubeClient, podName, namespace) {
@ -2404,7 +2377,7 @@ class KubernetesUtilities {
});
}
}
exports.default = KubernetesUtilities;
exports.default = KubernetesTaskRunner;
/***/ }),
@ -3247,8 +3220,7 @@ class ImageEnvironmentFactory {
if (p.value === '' || p.value === undefined) {
continue;
}
string += `--env ${p.name}="${p.value}" \
`;
string += `--env ${p.name}="${p.value}" `;
}
return string;
}

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -5,13 +5,13 @@ import { CloudRunnerProviderInterface } from '../services/cloud-runner-provider-
import CloudRunnerSecret from '../services/cloud-runner-secret';
import KubernetesStorage from './kubernetes-storage';
import CloudRunnerEnvironmentVariable from '../services/cloud-runner-environment-variable';
import KubernetesLogging from './kubernetes-logging';
import KubernetesTaskRunner from './kubernetes-task-runner';
import KubernetesSecret from './kubernetes-secret';
import KubernetesUtilities from './kubernetes-utils';
import waitUntil from 'async-wait-until';
import KubernetesJobSpecFactory from './kubernetes-job-spec-factory';
import KubernetesServiceAccount from './kubernetes-service-account';
import CloudRunnerLogger from '../services/cloud-runner-logger';
import { CoreV1Api } from '@kubernetes/client-node';
class Kubernetes implements CloudRunnerProviderInterface {
private kubeConfig: k8s.KubeConfig;
@ -99,13 +99,11 @@ class Kubernetes implements CloudRunnerProviderInterface {
CloudRunnerLogger.log('Creating build job');
await this.kubeClientBatch.createNamespacedJob(this.namespace, jobSpec);
CloudRunnerLogger.log('Job created');
this.setPodNameAndContainerName(
await KubernetesUtilities.findPodFromJob(this.kubeClient, this.jobName, this.namespace),
);
this.setPodNameAndContainerName(await Kubernetes.findPodFromJob(this.kubeClient, this.jobName, this.namespace));
CloudRunnerLogger.log('Watching pod until running');
await KubernetesUtilities.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
await KubernetesTaskRunner.watchUntilPodRunning(this.kubeClient, this.podName, this.namespace);
CloudRunnerLogger.log('Pod running, streaming logs');
await KubernetesLogging.streamLogs(
await KubernetesTaskRunner.runTask(
this.kubeConfig,
this.kubeClient,
this.jobName,
@ -164,5 +162,13 @@ class Kubernetes implements CloudRunnerProviderInterface {
) {
await this.kubeClient.deleteNamespacedPersistentVolumeClaim(this.pvcName, this.namespace);
}
static async findPodFromJob(kubeClient: CoreV1Api, jobName: string, namespace: string) {
const namespacedPods = await kubeClient.listNamespacedPod(namespace);
const pod = namespacedPods.body.items.find((x) => x.metadata?.labels?.['job-name'] === jobName);
if (pod === undefined) {
throw new Error("pod with job-name label doesn't exist");
}
return pod;
}
}
export default Kubernetes;

View File

@ -5,9 +5,10 @@ import { CloudRunnerState } from '../state/cloud-runner-state';
import * as core from '@actions/core';
import fs from 'fs';
import { CloudRunnerStatics } from '../cloud-runner-statics';
import waitUntil from 'async-wait-until';
class KubernetesLogging {
static async streamLogs(
class KubernetesTaskRunner {
static async runTask(
kubeConfig: KubeConfig,
kubeClient: CoreV1Api,
jobName: string,
@ -69,6 +70,24 @@ class KubernetesLogging {
}
CloudRunnerLogger.log('end of log stream');
}
static async watchUntilPodRunning(kubeClient: CoreV1Api, podName: string, namespace: string) {
let success: boolean = false;
CloudRunnerLogger.log(`Watching ${podName} ${namespace}`);
await waitUntil(
async () => {
const phase = (await kubeClient.readNamespacedPodStatus(podName, namespace))?.body.status?.phase;
success = phase === 'Running';
if (success || phase !== 'Pending') return true;
return false;
},
{
timeout: 500000,
intervalBetweenAttempts: 15000,
},
);
return success;
}
}
export default KubernetesLogging;
export default KubernetesTaskRunner;

View File

@ -1,33 +0,0 @@
import { CoreV1Api } from '@kubernetes/client-node';
import waitUntil from 'async-wait-until';
import CloudRunnerLogger from '../services/cloud-runner-logger';
class KubernetesUtilities {
static async findPodFromJob(kubeClient: CoreV1Api, jobName: string, namespace: string) {
const namespacedPods = await kubeClient.listNamespacedPod(namespace);
const pod = namespacedPods.body.items.find((x) => x.metadata?.labels?.['job-name'] === jobName);
if (pod === undefined) {
throw new Error("pod with job-name label doesn't exist");
}
return pod;
}
static async watchUntilPodRunning(kubeClient: CoreV1Api, podName: string, namespace: string) {
let success: boolean = false;
CloudRunnerLogger.log(`Watching ${podName} ${namespace}`);
await waitUntil(
async () => {
const phase = (await kubeClient.readNamespacedPodStatus(podName, namespace))?.body.status?.phase;
success = phase === 'Running';
if (success || phase !== 'Pending') return true;
return false;
},
{
timeout: 500000,
intervalBetweenAttempts: 15000,
},
);
return success;
}
}
export default KubernetesUtilities;

View File

@ -11,8 +11,7 @@ class ImageEnvironmentFactory {
if (p.value === '' || p.value === undefined) {
continue;
}
string += `--env ${p.name}="${p.value}" \
`;
string += `--env ${p.name}="${p.value}" `;
}
return string;
}