GitHub API integration, labelling, annotating namespace

This commit is contained in:
Koushik Dey
2020-07-22 20:07:30 +05:30
parent 2c8e7a02f5
commit 48110f8b94
16 changed files with 500 additions and 110 deletions
+111
View File
@@ -0,0 +1,111 @@
"use strict";
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 });
exports.sleepFor = exports.sendRequest = exports.WebRequestOptions = exports.WebResponse = exports.WebRequest = exports.StatusCodes = void 0;
// Taken from https://github.com/Azure/aks-set-context/blob/master/src/client.ts
const util = require("util");
const fs = require("fs");
const httpClient = require("typed-rest-client/HttpClient");
const core = require("@actions/core");
var httpCallbackClient = new httpClient.HttpClient('GITHUB_RUNNER', null, {});
var StatusCodes;
(function (StatusCodes) {
StatusCodes[StatusCodes["OK"] = 200] = "OK";
StatusCodes[StatusCodes["CREATED"] = 201] = "CREATED";
StatusCodes[StatusCodes["ACCEPTED"] = 202] = "ACCEPTED";
StatusCodes[StatusCodes["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
StatusCodes[StatusCodes["NOT_FOUND"] = 404] = "NOT_FOUND";
StatusCodes[StatusCodes["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
StatusCodes[StatusCodes["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
})(StatusCodes = exports.StatusCodes || (exports.StatusCodes = {}));
class WebRequest {
}
exports.WebRequest = WebRequest;
class WebResponse {
}
exports.WebResponse = WebResponse;
class WebRequestOptions {
}
exports.WebRequestOptions = WebRequestOptions;
function sendRequest(request, options) {
return __awaiter(this, void 0, void 0, function* () {
let i = 0;
let retryCount = options && options.retryCount ? options.retryCount : 5;
let retryIntervalInSeconds = options && options.retryIntervalInSeconds ? options.retryIntervalInSeconds : 2;
let retriableErrorCodes = options && options.retriableErrorCodes ? options.retriableErrorCodes : ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN"];
let retriableStatusCodes = options && options.retriableStatusCodes ? options.retriableStatusCodes : [408, 409, 500, 502, 503, 504];
let timeToWait = retryIntervalInSeconds;
while (true) {
try {
if (request.body && typeof (request.body) !== 'string' && !request.body["readable"]) {
request.body = fs.createReadStream(request.body["path"]);
}
let response = yield sendRequestInternal(request);
if (retriableStatusCodes.indexOf(response.statusCode) != -1 && ++i < retryCount) {
core.debug(util.format("Encountered a retriable status code: %s. Message: '%s'.", response.statusCode, response.statusMessage));
yield sleepFor(timeToWait);
timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds;
continue;
}
return response;
}
catch (error) {
if (retriableErrorCodes.indexOf(error.code) != -1 && ++i < retryCount) {
core.debug(util.format("Encountered a retriable error:%s. Message: %s.", error.code, error.message));
yield sleepFor(timeToWait);
timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds;
}
else {
if (error.code) {
core.debug("error code =" + error.code);
}
throw error;
}
}
}
});
}
exports.sendRequest = sendRequest;
function sleepFor(sleepDurationInSeconds) {
return new Promise((resolve, reject) => {
setTimeout(resolve, sleepDurationInSeconds * 1000);
});
}
exports.sleepFor = sleepFor;
function sendRequestInternal(request) {
return __awaiter(this, void 0, void 0, function* () {
core.debug(util.format("[%s]%s", request.method, request.uri));
var response = yield httpCallbackClient.request(request.method, request.uri, request.body, request.headers);
return yield toWebResponse(response);
});
}
function toWebResponse(response) {
return __awaiter(this, void 0, void 0, function* () {
var res = new WebResponse();
if (response) {
res.statusCode = response.message.statusCode;
res.statusMessage = response.message.statusMessage;
res.headers = response.message.headers;
var body = yield response.readBody();
if (body) {
try {
res.body = JSON.parse(body);
}
catch (error) {
core.debug("Could not parse response: " + JSON.stringify(error));
core.debug("Response: " + JSON.stringify(res.body));
res.body = body;
}
}
}
return res;
});
}
@@ -49,7 +49,7 @@ function deploy(kubectl, manifestFilePaths, deploymentStrategy) {
catch (e) {
core.debug("Unable to parse pods; Error: " + e);
}
annotateResources(deployedManifestFiles, kubectl, resourceTypes, allPods);
annotateAndLabelResources(deployedManifestFiles, kubectl, resourceTypes, allPods);
});
}
exports.deploy = deploy;
@@ -110,17 +110,29 @@ function checkManifestStability(kubectl, resources) {
yield KubernetesManifestUtility.checkManifestStability(kubectl, resources);
});
}
function annotateResources(files, kubectl, resourceTypes, allPods) {
const annotateResults = [];
annotateResults.push(utility_1.annotateNamespace(kubectl, TaskInputParameters.namespace));
annotateResults.push(kubectl.annotateFiles(files, models.workflowAnnotations, true));
resourceTypes.forEach(resource => {
if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) {
utility_1.annotateChildPods(kubectl, resource.type, resource.name, allPods)
.forEach(execResult => annotateResults.push(execResult));
}
function annotateAndLabelResources(files, kubectl, resourceTypes, allPods) {
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel();
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel);
labelResources(files, kubectl, annotationKeyLabel);
}
function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey) {
return __awaiter(this, void 0, void 0, function* () {
const annotateResults = [];
const lastSuccessSha = yield utility_1.getLastSuccessfulRunSha(TaskInputParameters.githubToken);
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha);
annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, [annotationKeyValStr], true));
annotateResults.push(kubectl.annotateFiles(files, [annotationKeyValStr], true));
resourceTypes.forEach(resource => {
if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) {
utility_1.annotateChildPods(kubectl, resource.type, resource.name, annotationKeyValStr, allPods)
.forEach(execResult => annotateResults.push(execResult));
}
});
utility_1.checkForErrors(annotateResults, true);
});
utility_1.checkForErrors(annotateResults, true);
}
function labelResources(files, kubectl, label) {
utility_1.checkForErrors([kubectl.labelFiles(files, [`workflow=${label}`], true)], true);
}
function updateResourceObjects(filePaths, imagePullSecrets, containers) {
const newObjectsList = [];
+42 -29
View File
@@ -1,9 +1,19 @@
"use strict";
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 });
exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.annotateNamespace = exports.annotateChildPods = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0;
exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.annotateChildPods = exports.getLastSuccessfulRunSha = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0;
const os = require("os");
const core = require("@actions/core");
const constants_1 = require("../constants");
const githubClient_1 = require("../githubClient");
const httpClient_1 = require("./httpClient");
function getExecutableExtension() {
if (os.type().match(/^Win/)) {
return '.exe';
@@ -18,7 +28,7 @@ function isEqual(str1, str2, ignoreCase) {
if (str1 == null || str2 == null) {
return false;
}
if (ignoreCase) {
if (!!ignoreCase) {
return str1.toUpperCase() === str2.toUpperCase();
}
else {
@@ -30,7 +40,7 @@ function checkForErrors(execResults, warnIfError) {
if (execResults.length !== 0) {
let stderr = '';
execResults.forEach(result => {
if (result && result.stderr) {
if (!!result && !!result.stderr) {
if (result.code !== 0) {
stderr += result.stderr + '\n';
}
@@ -40,7 +50,7 @@ function checkForErrors(execResults, warnIfError) {
}
});
if (stderr.length > 0) {
if (warnIfError) {
if (!!warnIfError) {
core.warning(stderr.trim());
}
else {
@@ -50,19 +60,42 @@ function checkForErrors(execResults, warnIfError) {
}
}
exports.checkForErrors = checkForErrors;
function annotateChildPods(kubectl, resourceType, resourceName, allPods) {
function getLastSuccessfulRunSha(githubToken) {
return __awaiter(this, void 0, void 0, function* () {
let lastSuccessRunSha = '';
const gitHubClient = new githubClient_1.GitHubClient(process.env.GITHUB_REPOSITORY, githubToken);
const branch = process.env.GITHUB_REF.replace("refs/heads/", "");
const response = yield gitHubClient.getSuccessfulRunsOnBranch(branch);
if (response.statusCode == httpClient_1.StatusCodes.OK
&& response.body
&& response.body.total_count) {
if (response.body.total_count > 0) {
lastSuccessRunSha = response.body.workflow_runs[0].head_sha;
}
else {
lastSuccessRunSha = 'NA';
}
}
else if (response.statusCode != httpClient_1.StatusCodes.OK) {
core.debug(`An error occured while getting succeessful run results. Statuscode: ${response.statusCode}, StatusMessage: ${response.statusMessage}`);
}
return lastSuccessRunSha;
});
}
exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha;
function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyValStr, allPods) {
const commandExecutionResults = [];
let owner = resourceName;
if (resourceType.toLowerCase().indexOf('deployment') > -1) {
owner = kubectl.getNewReplicaSet(resourceName);
}
if (allPods && allPods.items && allPods.items.length > 0) {
if (!!allPods && !!allPods.items && allPods.items.length > 0) {
allPods.items.forEach((pod) => {
const owners = pod.metadata.ownerReferences;
if (owners) {
if (!!owners) {
owners.forEach(ownerRef => {
if (ownerRef.name === owner) {
commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, constants_1.workflowAnnotations, true));
commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, [annotationKeyValStr], true));
}
});
}
@@ -71,26 +104,6 @@ function annotateChildPods(kubectl, resourceType, resourceName, allPods) {
return commandExecutionResults;
}
exports.annotateChildPods = annotateChildPods;
function annotateNamespace(kubectl, namespaceName) {
const result = kubectl.getResource('namespace', namespaceName);
if (!result) {
return { code: -1, stderr: 'Failed to get resource' };
}
else if (result && result.stderr) {
return result;
}
if (result && result.stdout) {
const annotationsSet = JSON.parse(result.stdout).metadata.annotations;
if (annotationsSet && annotationsSet.runUri) {
if (annotationsSet.runUri.indexOf(process.env['GITHUB_REPOSITORY']) == -1) {
core.debug(`Skipping 'annotate namespace' as namespace annotated by other workflow`);
return { code: 0, stdout: '' };
}
}
return kubectl.annotate('namespace', namespaceName, constants_1.workflowAnnotations, true);
}
}
exports.annotateNamespace = annotateNamespace;
function sleep(timeout) {
return new Promise(resolve => setTimeout(resolve, timeout));
}