New traceability fields added to annotations (#90)

* New traceability fields
This commit is contained in:
Jyotsna
2020-12-30 15:03:39 +05:30
committed by GitHub
parent 51b95a5ca2
commit 04921d7d06
9 changed files with 257 additions and 46 deletions
+20 -15
View File
@@ -1,4 +1,5 @@
'use strict';
import { DeploymentConfig } from "./utilities/utility";
export class KubernetesWorkload {
public static pod: string = 'Pod';
@@ -25,21 +26,25 @@ export const deploymentTypes: string[] = ['deployment', 'replicaset', 'daemonset
export const workloadTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
export const workloadTypesWithRolloutStatus: string[] = ['deployment', 'daemonset', 'statefulset'];
export function getWorkflowAnnotationsJson(lastSuccessRunSha: string, workflowFilePath: string): string {
return `{`
+ `'run': '${process.env.GITHUB_RUN_ID}',`
+ `'repository': '${process.env.GITHUB_REPOSITORY}',`
+ `'workflow': '${process.env.GITHUB_WORKFLOW}',`
+ `'workflowFileName': '${workflowFilePath.replace(".github/workflows/", "")}',`
+ `'jobName': '${process.env.GITHUB_JOB}',`
+ `'createdBy': '${process.env.GITHUB_ACTOR}',`
+ `'runUri': 'https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}',`
+ `'commit': '${process.env.GITHUB_SHA}',`
+ `'lastSuccessRunCommit': '${lastSuccessRunSha}',`
+ `'branch': '${process.env.GITHUB_REF}',`
+ `'deployTimestamp': '${Date.now()}',`
+ `'provider': 'GitHub'`
+ `}`;
export function getWorkflowAnnotationsJson(lastSuccessRunSha: string, workflowFilePath: string, deploymentConfig: DeploymentConfig): string {
let annotationObject: any = {};
annotationObject["run"] = process.env.GITHUB_RUN_ID;
annotationObject["repository"] = process.env.GITHUB_REPOSITORY;
annotationObject["workflow"] = process.env.GITHUB_WORKFLOW;
annotationObject["workflowFileName"] = workflowFilePath.replace(".github/workflows/", "");
annotationObject["jobName"] = process.env.GITHUB_JOB;
annotationObject["createdBy"] = process.env.GITHUB_ACTOR;
annotationObject["runUri"] = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
annotationObject["commit"] = process.env.GITHUB_SHA;
annotationObject["lastSuccessRunCommit"] = lastSuccessRunSha;
annotationObject["branch"] = process.env.GITHUB_REF;
annotationObject["deployTimestamp"] = Date.now();
annotationObject["dockerfilePaths"] = deploymentConfig.dockerfilePaths;
annotationObject["manifestsPaths"] = deploymentConfig.manifestFilePaths
annotationObject["helmChartPaths"] = deploymentConfig.helmChartFilePaths;
annotationObject["provider"] = "GitHub";
return JSON.stringify(annotationObject);
}
export function getWorkflowAnnotationKeyLabel(workflowFilePath: string): string {
+33
View File
@@ -0,0 +1,33 @@
import { ToolRunner, IExecOptions, IExecSyncResult } from "./utilities/tool-runner";
export class DockerExec {
private dockerPath: string;
constructor(dockerPath: string) {
this.dockerPath = dockerPath;
};
public pull(image: string, args: string[], silent?: boolean) {
args = ['pull', image, ...args];
let result: IExecSyncResult = this.execute(args, silent);
if (result.stderr != '' && result.code != 0) {
throw new Error(`docker images pull failed with: ${result.error}`);
}
}
public inspect(image: string, args: string[], silent?: boolean): any {
args = ['inspect', image, ...args];
let result: IExecSyncResult = this.execute(args, silent);
if (result.stderr != '' && result.code != 0) {
throw new Error(`docker inspect call failed with: ${result.error}`);
}
return result.stdout;
}
private execute(args: string[], silent?: boolean) {
const command = new ToolRunner(this.dockerPath);
command.arg(args);
return command.execSync({ silent: !!silent } as IExecOptions);
}
}
@@ -17,7 +17,7 @@ import { IExecSyncResult } from '../../utilities/tool-runner';
import { deployPodCanary } from './pod-canary-deployment-helper';
import { deploySMICanary } from './smi-canary-deployment-helper';
import { checkForErrors, annotateChildPods, getWorkflowFilePath, getLastSuccessfulRunSha } from "../utility";
import { checkForErrors, annotateChildPods, getWorkflowFilePath, getLastSuccessfulRunSha, DeploymentConfig, getDeploymentConfig } from "../utility";
import { isBlueGreenDeploymentStrategy, isIngressRoute, isSMIRoute, routeBlueGreen } from './blue-green-helper';
import { deployBlueGreenService } from './service-blue-green-helper';
import { deployBlueGreenIngress } from './ingress-blue-green-helper';
@@ -45,7 +45,7 @@ export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], depl
ingressResources.forEach(ingressResource => {
kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name);
});
// annotate resources
let allPods: any;
try {
@@ -79,7 +79,7 @@ function deployManifests(files: string[], kubectl: Kubectl, isCanaryDeploymentSt
result = canaryDeploymentOutput.result;
files = canaryDeploymentOutput.newFilePaths;
} else if (isBlueGreenDeploymentStrategy) {
let blueGreenDeploymentOutput: any;
let blueGreenDeploymentOutput: any;
if (isIngressRoute()) {
blueGreenDeploymentOutput = deployBlueGreenIngress(kubectl, files);
} else if (isSMIRoute()) {
@@ -130,15 +130,16 @@ async function checkManifestStability(kubectl: Kubectl, resources: Resource[]):
async function annotateAndLabelResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any) {
const workflowFilePath = await getWorkflowFilePath(TaskInputParameters.githubToken);
const deploymentConfig = await getDeploymentConfig();
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath);
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, deploymentConfig);
labelResources(files, kubectl, annotationKeyLabel);
}
function annotateResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any, annotationKey: string, workflowFilePath: string) {
function annotateResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any, annotationKey: string, workflowFilePath: string, deploymentConfig: DeploymentConfig) {
const annotateResults: IExecSyncResult[] = [];
const lastSuccessSha = getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey);
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath);
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath, deploymentConfig);
annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, annotationKeyValStr));
annotateResults.push(kubectl.annotateFiles(files, annotationKeyValStr));
resourceTypes.forEach(resource => {
+71
View File
@@ -4,6 +4,15 @@ import { IExecSyncResult } from './tool-runner';
import { Kubectl } from '../kubectl-object-model';
import { GitHubClient } from '../githubClient';
import { StatusCodes } from "./httpClient";
import * as inputParams from "../input-parameters";
import { DockerExec } from '../docker-object-model';
import * as io from '@actions/io';
export interface DeploymentConfig {
manifestFilePaths: string[];
helmChartFilePaths: string[];
dockerfilePaths: any;
}
export function getExecutableExtension(): string {
if (os.type().match(/^Win/)) {
@@ -129,6 +138,39 @@ export function annotateChildPods(kubectl: Kubectl, resourceType: string, resour
return commandExecutionResults;
}
export async function getDeploymentConfig(): Promise<DeploymentConfig> {
const inputManifestFiles = inputParams.manifests || [];
const helmChartPaths = (process.env.HELM_CHART_PATHS && process.env.HELM_CHART_PATHS.split('\n').filter(path => path != "")) || [];
const imageNames = inputParams.containers || [];
let imageDockerfilePathMap: { [id: string]: string; } = {};
//Fetching from image label if available
for (const image of imageNames) {
let imageConfig: any, imageInspectResult: string;
try {
await checkDockerPath();
var dockerExec: DockerExec = new DockerExec('docker');
dockerExec.pull(image, [], true);
imageInspectResult = dockerExec.inspect(image, [], true);
imageConfig = JSON.parse(imageInspectResult)[0];
imageDockerfilePathMap[image] = getDockerfilePath(imageConfig);
}
catch (ex) {
core.warning(`Failed to get dockerfile path for image ${image.toString()} | ` + ex);
}
}
const deploymentConfig = <DeploymentConfig>{
manifestFilePaths: inputManifestFiles,
helmChartFilePaths: helmChartPaths,
dockerfilePaths: imageDockerfilePathMap
};
return Promise.resolve(deploymentConfig);
}
export function sleep(timeout: number) {
return new Promise(resolve => setTimeout(resolve, timeout));
}
@@ -140,3 +182,32 @@ export function getRandomInt(max: number) {
export function getCurrentTime(): number {
return new Date().getTime();
}
async function checkDockerPath() {
let dockerPath = await io.which('docker', false);
if (!dockerPath) {
throw new Error('Docker is not installed.');
}
}
function getDockerfilePath(imageConfig: any): string {
const DOCKERFILE_PATH_LABEL_KEY = 'dockerfile-path';
const ref: string = process.env.GITHUB_REF && process.env.GITHUB_REF.replace('refs/heads/', '').replace('refs/tags/', '');
let pathLabel: string, pathLink: string, pathValue: string = '';
if (imageConfig) {
if ((imageConfig.Config) && (imageConfig.Config.Labels) && (imageConfig.Config.Labels[DOCKERFILE_PATH_LABEL_KEY])) {
pathLabel = imageConfig.Config.Labels[DOCKERFILE_PATH_LABEL_KEY];
if (pathValue.startsWith('./')) { //if it is relative filepath convert to link from current repo
pathLink = `https://github.com/${process.env.GITHUB_REPOSITORY}/blob/${ref}/${pathLabel}`;
pathValue = pathLink;
}
else {
pathValue = pathLabel;
}
}
else {
pathValue = '';
}
}
return pathValue;
}