Integrated workflow list and run API to get lastSuccessRunCommit

This commit is contained in:
Koushik Dey 2020-08-05 10:43:46 +05:30
parent 3919a9ee22
commit 3d1e46f18f
9 changed files with 151 additions and 63 deletions

View File

@ -384,7 +384,6 @@ test("utility - getLastSuccessfulRunSha() - Get Commits under different conditio
if (name == errorWebRequest) if (name == errorWebRequest)
return Promise.resolve(errorWebResponse); return Promise.resolve(errorWebResponse);
});*/ });*/
jest.spyOn(httpClient, 'sendRequest').mockImplementation(() => Promise.resolve(lastSuccessfulRunUrlResponse)); jest.spyOn(httpClient, 'sendRequest').mockImplementation(() => Promise.resolve(lastSuccessfulRunUrlResponse));
//Invoke and assert //Invoke and assert
await expect(utility.getLastSuccessfulRunSha(process.env.GITHUB_TOKEN)).resolves.not.toThrowError; await expect(utility.getLastSuccessfulRunSha(process.env.GITHUB_TOKEN)).resolves.not.toThrowError;

View File

@ -41,9 +41,9 @@ function getWorkflowAnnotationsJson(lastSuccessRunSha) {
+ `}`; + `}`;
} }
exports.getWorkflowAnnotationsJson = getWorkflowAnnotationsJson; exports.getWorkflowAnnotationsJson = getWorkflowAnnotationsJson;
function getWorkflowAnnotationKeyLabel() { function getWorkflowAnnotationKeyLabel(workflowFilePath) {
const hashKey = require("crypto").createHash("MD5") const hashKey = require("crypto").createHash("MD5")
.update(`${process.env.GITHUB_REPOSITORY}/${process.env.GITHUB_WORKFLOW}`) .update(`${process.env.GITHUB_REPOSITORY}/${workflowFilePath}`)
.digest("hex"); .digest("hex");
return `githubWorkflow_${hashKey}`; return `githubWorkflow_${hashKey}`;
} }

View File

@ -17,10 +17,10 @@ class GitHubClient {
this._repository = repository; this._repository = repository;
this._token = token; this._token = token;
} }
getSuccessfulRunsOnBranch(branch, force) { getSuccessfulRunsOnBranch(branch, workflowFileName, force) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
if (force || !this._successfulRunsOnBranchPromise) { if (force || !this._successfulRunsOnBranchPromise) {
const lastSuccessfulRunUrl = `https://api.github.com/repos/${this._repository}/actions/runs?status=success&branch=${branch}`; const lastSuccessfulRunUrl = `https://api.github.com/repos/${this._repository}/actions/workflows/${workflowFileName}/runs?status=success&branch=${branch}`;
const webRequest = new httpClient_1.WebRequest(); const webRequest = new httpClient_1.WebRequest();
webRequest.method = "GET"; webRequest.method = "GET";
webRequest.uri = lastSuccessfulRunUrl; webRequest.uri = lastSuccessfulRunUrl;
@ -34,5 +34,22 @@ class GitHubClient {
return this._successfulRunsOnBranchPromise; return this._successfulRunsOnBranchPromise;
}); });
} }
getWorkflows(force) {
return __awaiter(this, void 0, void 0, function* () {
if (force || !this._workflowsPromise) {
const getWorkflowFileNameUrl = `https://api.github.com/repos/${this._repository}/actions/workflows`;
const webRequest = new httpClient_1.WebRequest();
webRequest.method = "GET";
webRequest.uri = getWorkflowFileNameUrl;
webRequest.headers = {
Authorization: `Bearer ${this._token}`
};
core.debug(`Getting workflows for repo: ${this._repository}`);
const response = yield httpClient_1.sendRequest(webRequest);
this._workflowsPromise = Promise.resolve(response);
}
return this._workflowsPromise;
});
}
} }
exports.GitHubClient = GitHubClient; exports.GitHubClient = GitHubClient;

View File

@ -111,14 +111,16 @@ function checkManifestStability(kubectl, resources) {
}); });
} }
function annotateAndLabelResources(files, kubectl, resourceTypes, allPods) { function annotateAndLabelResources(files, kubectl, resourceTypes, allPods) {
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(); return __awaiter(this, void 0, void 0, function* () {
const workflowFilePath = yield utility_1.getWorkflowFilePath(TaskInputParameters.githubToken);
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel); annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel);
labelResources(files, kubectl, annotationKeyLabel); labelResources(files, kubectl, annotationKeyLabel);
});
} }
function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey) { function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey) {
return __awaiter(this, void 0, void 0, function* () {
const annotateResults = []; const annotateResults = [];
const lastSuccessSha = yield utility_1.getLastSuccessfulRunSha(TaskInputParameters.githubToken); const lastSuccessSha = utility_1.getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey);
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha); let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha);
annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, [annotationKeyValStr], true)); annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, [annotationKeyValStr], true));
annotateResults.push(kubectl.annotateFiles(files, [annotationKeyValStr], true)); annotateResults.push(kubectl.annotateFiles(files, [annotationKeyValStr], true));
@ -129,7 +131,6 @@ function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey
} }
}); });
utility_1.checkForErrors(annotateResults, true); utility_1.checkForErrors(annotateResults, true);
});
} }
function labelResources(files, kubectl, label) { function labelResources(files, kubectl, label) {
utility_1.checkForErrors([kubectl.labelFiles(files, [`workflow=${label}`], true)], true); utility_1.checkForErrors([kubectl.labelFiles(files, [`workflow=${label}`], true)], true);

View File

@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.annotateChildPods = exports.getLastSuccessfulRunSha = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0; exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.annotateChildPods = exports.getWorkflowFilePath = exports.getLastSuccessfulRunSha = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0;
const os = require("os"); const os = require("os");
const core = require("@actions/core"); const core = require("@actions/core");
const githubClient_1 = require("../githubClient"); const githubClient_1 = require("../githubClient");
@ -60,29 +60,55 @@ function checkForErrors(execResults, warnIfError) {
} }
} }
exports.checkForErrors = checkForErrors; exports.checkForErrors = checkForErrors;
function getLastSuccessfulRunSha(githubToken) { function getLastSuccessfulRunSha(kubectl, namespaceName, annotationKey) {
const result = kubectl.getResource('namespace', namespaceName);
if (!result) {
core.debug(`Failed to get commits from cluster.`);
return '';
}
else {
if (result.stderr) {
core.debug(`${result.stderr}`);
return process.env.GITHUB_SHA;
}
else if (result.stdout) {
const annotationsSet = JSON.parse(result.stdout).metadata.annotations;
if (!!annotationsSet[annotationKey]) {
return JSON.parse(annotationsSet[annotationKey].replace(/'/g, '"')).commit;
}
else {
return 'NA';
}
}
}
}
exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha;
function getWorkflowFilePath(githubToken) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let lastSuccessRunSha = ''; let workflowFilePath = process.env.GITHUB_WORKFLOW;
const gitHubClient = new githubClient_1.GitHubClient(process.env.GITHUB_REPOSITORY, githubToken); if (!workflowFilePath.startsWith('.github/workflows/')) {
const branch = process.env.GITHUB_REF.replace("refs/heads/", ""); const githubClient = new githubClient_1.GitHubClient(process.env.GITHUB_REPOSITORY, githubToken);
const response = yield gitHubClient.getSuccessfulRunsOnBranch(branch); const response = yield githubClient.getWorkflows();
if (response.statusCode == httpClient_1.StatusCodes.OK if (response.statusCode == httpClient_1.StatusCodes.OK
&& !!response.body && !!response.body
&& !!response.body.total_count) { && !!response.body.total_count) {
if (response.body.total_count > 0) { if (response.body.total_count > 0) {
lastSuccessRunSha = response.body.workflow_runs[0].head_sha; for (let workflow of response.body.workflows) {
if (process.env.GITHUB_WORKFLOW === workflow.name) {
workflowFilePath = workflow.path;
break;
}
} }
else {
lastSuccessRunSha = 'NA';
} }
} }
else if (response.statusCode != httpClient_1.StatusCodes.OK) { else if (response.statusCode != httpClient_1.StatusCodes.OK) {
core.debug(`An error occured while getting succeessful run results. Statuscode: ${response.statusCode}, StatusMessage: ${response.statusMessage}`); core.debug(`An error occured while getting list of workflows on the repo. Statuscode: ${response.statusCode}, StatusMessage: ${response.statusMessage}`);
} }
return Promise.resolve(lastSuccessRunSha); }
return Promise.resolve(workflowFilePath);
}); });
} }
exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha; exports.getWorkflowFilePath = getWorkflowFilePath;
function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyValStr, allPods) { function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyValStr, allPods) {
const commandExecutionResults = []; const commandExecutionResults = [];
let owner = resourceName; let owner = resourceName;

View File

@ -40,9 +40,9 @@ export function getWorkflowAnnotationsJson(lastSuccessRunSha: string): string {
+ `'provider': 'GitHub'` + `'provider': 'GitHub'`
+ `}`; + `}`;
} }
export function getWorkflowAnnotationKeyLabel(): string { export function getWorkflowAnnotationKeyLabel(workflowFilePath: string): string {
const hashKey = require("crypto").createHash("MD5") const hashKey = require("crypto").createHash("MD5")
.update(`${process.env.GITHUB_REPOSITORY}/${process.env.GITHUB_WORKFLOW}`) .update(`${process.env.GITHUB_REPOSITORY}/${workflowFilePath}`)
.digest("hex"); .digest("hex");
return `githubWorkflow_${hashKey}`; return `githubWorkflow_${hashKey}`;
} }

View File

@ -7,9 +7,9 @@ export class GitHubClient {
this._token = token; this._token = token;
} }
public async getSuccessfulRunsOnBranch(branch: string, force?: boolean): Promise<any> { public async getSuccessfulRunsOnBranch(branch: string, workflowFileName: string, force?: boolean): Promise<any> {
if (force || !this._successfulRunsOnBranchPromise) { if (force || !this._successfulRunsOnBranchPromise) {
const lastSuccessfulRunUrl = `https://api.github.com/repos/${this._repository}/actions/runs?status=success&branch=${branch}`; const lastSuccessfulRunUrl = `https://api.github.com/repos/${this._repository}/actions/workflows/${workflowFileName}/runs?status=success&branch=${branch}`;
const webRequest = new WebRequest(); const webRequest = new WebRequest();
webRequest.method = "GET"; webRequest.method = "GET";
webRequest.uri = lastSuccessfulRunUrl; webRequest.uri = lastSuccessfulRunUrl;
@ -24,7 +24,25 @@ export class GitHubClient {
return this._successfulRunsOnBranchPromise; return this._successfulRunsOnBranchPromise;
} }
public async getWorkflows(force?: boolean): Promise<any> {
if (force || !this._workflowsPromise) {
const getWorkflowFileNameUrl = `https://api.github.com/repos/${this._repository}/actions/workflows`;
const webRequest = new WebRequest();
webRequest.method = "GET";
webRequest.uri = getWorkflowFileNameUrl;
webRequest.headers = {
Authorization: `Bearer ${this._token}`
};
core.debug(`Getting workflows for repo: ${this._repository}`);
const response: WebResponse = await sendRequest(webRequest);
this._workflowsPromise = Promise.resolve(response);
}
return this._workflowsPromise;
}
private _repository: string; private _repository: string;
private _token: string; private _token: string;
private _successfulRunsOnBranchPromise: Promise<any>; private _successfulRunsOnBranchPromise: Promise<any>;
private _workflowsPromise: Promise<any>;
} }

View File

@ -17,7 +17,7 @@ import { IExecSyncResult } from '../../utilities/tool-runner';
import { deployPodCanary } from './pod-canary-deployment-helper'; import { deployPodCanary } from './pod-canary-deployment-helper';
import { deploySMICanary } from './smi-canary-deployment-helper'; import { deploySMICanary } from './smi-canary-deployment-helper';
import { checkForErrors, annotateChildPods, getLastSuccessfulRunSha } from "../utility"; import { checkForErrors, annotateChildPods, getWorkflowFilePath, getLastSuccessfulRunSha } from "../utility";
export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], deploymentStrategy: string) { export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], deploymentStrategy: string) {
@ -112,15 +112,16 @@ async function checkManifestStability(kubectl: Kubectl, resources: Resource[]):
await KubernetesManifestUtility.checkManifestStability(kubectl, resources); await KubernetesManifestUtility.checkManifestStability(kubectl, resources);
} }
function annotateAndLabelResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any) { async function annotateAndLabelResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any) {
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(); const workflowFilePath = await getWorkflowFilePath(TaskInputParameters.githubToken);
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel); annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel);
labelResources(files, kubectl, annotationKeyLabel); labelResources(files, kubectl, annotationKeyLabel);
} }
async function annotateResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any, annotationKey: string) { function annotateResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any, annotationKey: string) {
const annotateResults: IExecSyncResult[] = []; const annotateResults: IExecSyncResult[] = [];
const lastSuccessSha = await getLastSuccessfulRunSha(TaskInputParameters.githubToken); const lastSuccessSha = getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey);
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha); let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha);
annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, [annotationKeyValStr], true)); annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, [annotationKeyValStr], true));
annotateResults.push(kubectl.annotateFiles(files, [annotationKeyValStr], true)); annotateResults.push(kubectl.annotateFiles(files, [annotationKeyValStr], true));

View File

@ -51,25 +51,51 @@ export function checkForErrors(execResults: IExecSyncResult[], warnIfError?: boo
} }
} }
export async function getLastSuccessfulRunSha(githubToken: string): Promise<string> { export function getLastSuccessfulRunSha(kubectl: Kubectl, namespaceName: string, annotationKey: string): string {
let lastSuccessRunSha = ''; const result = kubectl.getResource('namespace', namespaceName);
const gitHubClient = new GitHubClient(process.env.GITHUB_REPOSITORY, githubToken); if (!result) {
const branch = process.env.GITHUB_REF.replace("refs/heads/", ""); core.debug(`Failed to get commits from cluster.`);
const response = await gitHubClient.getSuccessfulRunsOnBranch(branch); return '';
}
else {
if (result.stderr) {
core.debug(`${result.stderr}`);
return process.env.GITHUB_SHA;
}
else if (result.stdout) {
const annotationsSet = JSON.parse(result.stdout).metadata.annotations;
if (!!annotationsSet[annotationKey]) {
return JSON.parse(annotationsSet[annotationKey].replace(/'/g, '"')).commit;
}
else {
return 'NA';
}
}
}
}
export async function getWorkflowFilePath(githubToken: string): Promise<string> {
let workflowFilePath = process.env.GITHUB_WORKFLOW;
if (!workflowFilePath.startsWith('.github/workflows/')) {
const githubClient = new GitHubClient(process.env.GITHUB_REPOSITORY, githubToken);
const response = await githubClient.getWorkflows();
if (response.statusCode == StatusCodes.OK if (response.statusCode == StatusCodes.OK
&& !!response.body && !!response.body
&& !!response.body.total_count) { && !!response.body.total_count) {
if (response.body.total_count > 0) { if (response.body.total_count > 0) {
lastSuccessRunSha = response.body.workflow_runs[0].head_sha; for (let workflow of response.body.workflows) {
if (process.env.GITHUB_WORKFLOW === workflow.name) {
workflowFilePath = workflow.path;
break;
}
} }
else {
lastSuccessRunSha = 'NA';
} }
} }
else if (response.statusCode != StatusCodes.OK) { else if (response.statusCode != StatusCodes.OK) {
core.debug(`An error occured while getting succeessful run results. Statuscode: ${response.statusCode}, StatusMessage: ${response.statusMessage}`); core.debug(`An error occured while getting list of workflows on the repo. Statuscode: ${response.statusCode}, StatusMessage: ${response.statusMessage}`);
} }
return Promise.resolve(lastSuccessRunSha); }
return Promise.resolve(workflowFilePath);
} }
export function annotateChildPods(kubectl: Kubectl, resourceType: string, resourceName: string, annotationKeyValStr: string, allPods): IExecSyncResult[] { export function annotateChildPods(kubectl: Kubectl, resourceType: string, resourceName: string, annotationKeyValStr: string, allPods): IExecSyncResult[] {