Initial commit for config files annotations

This commit is contained in:
Jyotsna 2020-11-17 18:15:45 +05:30
parent b371791f3a
commit bfca26e368
10 changed files with 11699 additions and 5158 deletions

View File

@ -1,51 +1,52 @@
'use strict'; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getWorkflowAnnotationKeyLabel = exports.getWorkflowAnnotationsJson = exports.workloadTypesWithRolloutStatus = exports.workloadTypes = exports.deploymentTypes = exports.ServiceTypes = exports.DiscoveryAndLoadBalancerResource = exports.KubernetesWorkload = void 0; exports.getWorkflowAnnotationKeyLabel = exports.getWorkflowAnnotationsJson = exports.workloadTypesWithRolloutStatus = exports.workloadTypes = exports.deploymentTypes = exports.ServiceTypes = exports.DiscoveryAndLoadBalancerResource = exports.KubernetesWorkload = void 0;
class KubernetesWorkload { class KubernetesWorkload {
} }
exports.KubernetesWorkload = KubernetesWorkload; exports.KubernetesWorkload = KubernetesWorkload;
KubernetesWorkload.pod = 'Pod'; KubernetesWorkload.pod = 'Pod';
KubernetesWorkload.replicaset = 'Replicaset'; KubernetesWorkload.replicaset = 'Replicaset';
KubernetesWorkload.deployment = 'Deployment'; KubernetesWorkload.deployment = 'Deployment';
KubernetesWorkload.statefulSet = 'StatefulSet'; KubernetesWorkload.statefulSet = 'StatefulSet';
KubernetesWorkload.daemonSet = 'DaemonSet'; KubernetesWorkload.daemonSet = 'DaemonSet';
KubernetesWorkload.job = 'job'; KubernetesWorkload.job = 'job';
KubernetesWorkload.cronjob = 'cronjob'; KubernetesWorkload.cronjob = 'cronjob';
class DiscoveryAndLoadBalancerResource { class DiscoveryAndLoadBalancerResource {
} }
exports.DiscoveryAndLoadBalancerResource = DiscoveryAndLoadBalancerResource; exports.DiscoveryAndLoadBalancerResource = DiscoveryAndLoadBalancerResource;
DiscoveryAndLoadBalancerResource.service = 'service'; DiscoveryAndLoadBalancerResource.service = 'service';
DiscoveryAndLoadBalancerResource.ingress = 'ingress'; DiscoveryAndLoadBalancerResource.ingress = 'ingress';
class ServiceTypes { class ServiceTypes {
} }
exports.ServiceTypes = ServiceTypes; exports.ServiceTypes = ServiceTypes;
ServiceTypes.loadBalancer = 'LoadBalancer'; ServiceTypes.loadBalancer = 'LoadBalancer';
ServiceTypes.nodePort = 'NodePort'; ServiceTypes.nodePort = 'NodePort';
ServiceTypes.clusterIP = 'ClusterIP'; ServiceTypes.clusterIP = 'ClusterIP';
exports.deploymentTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset']; exports.deploymentTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset'];
exports.workloadTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob']; exports.workloadTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
exports.workloadTypesWithRolloutStatus = ['deployment', 'daemonset', 'statefulset']; exports.workloadTypesWithRolloutStatus = ['deployment', 'daemonset', 'statefulset'];
function getWorkflowAnnotationsJson(lastSuccessRunSha, workflowFilePath) { function getWorkflowAnnotationsJson(lastSuccessRunSha, workflowFilePath, filePathConfigs) {
return `{` return `{`
+ `'run': '${process.env.GITHUB_RUN_ID}',` + `'run': '${process.env.GITHUB_RUN_ID}',`
+ `'repository': '${process.env.GITHUB_REPOSITORY}',` + `'repository': '${process.env.GITHUB_REPOSITORY}',`
+ `'workflow': '${process.env.GITHUB_WORKFLOW}',` + `'workflow': '${process.env.GITHUB_WORKFLOW}',`
+ `'workflowFileName': '${workflowFilePath.replace(".github/workflows/", "")}',` + `'workflowFileName': '${workflowFilePath.replace(".github/workflows/", "")}',`
+ `'jobName': '${process.env.GITHUB_JOB}',` + `'jobName': '${process.env.GITHUB_JOB}',`
+ `'createdBy': '${process.env.GITHUB_ACTOR}',` + `'createdBy': '${process.env.GITHUB_ACTOR}',`
+ `'runUri': 'https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}',` + `'runUri': 'https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}',`
+ `'commit': '${process.env.GITHUB_SHA}',` + `'commit': '${process.env.GITHUB_SHA}',`
+ `'lastSuccessRunCommit': '${lastSuccessRunSha}',` + `'lastSuccessRunCommit': '${lastSuccessRunSha}',`
+ `'branch': '${process.env.GITHUB_REF}',` + `'branch': '${process.env.GITHUB_REF}',`
+ `'deployTimestamp': '${Date.now()}',` + `'deployTimestamp': '${Date.now()}',`
+ `'provider': 'GitHub'` + `'filePathConfigs': '${JSON.stringify(filePathConfigs)}',`
+ `}`; + `'provider': 'GitHub'`
} + `}`;
exports.getWorkflowAnnotationsJson = getWorkflowAnnotationsJson; }
function getWorkflowAnnotationKeyLabel(workflowFilePath) { exports.getWorkflowAnnotationsJson = getWorkflowAnnotationsJson;
const hashKey = require("crypto").createHash("MD5") function getWorkflowAnnotationKeyLabel(workflowFilePath) {
.update(`${process.env.GITHUB_REPOSITORY}/${workflowFilePath}`) const hashKey = require("crypto").createHash("MD5")
.digest("hex"); .update(`${process.env.GITHUB_REPOSITORY}/${workflowFilePath}`)
return `githubWorkflow_${hashKey}`; .digest("hex");
} return `githubWorkflow_${hashKey}`;
exports.getWorkflowAnnotationKeyLabel = getWorkflowAnnotationKeyLabel; }
exports.getWorkflowAnnotationKeyLabel = getWorkflowAnnotationKeyLabel;

35
lib/utilities/exec.js Normal file
View File

@ -0,0 +1,35 @@
"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.exec = void 0;
const aexec = require("@actions/exec");
exports.exec = (command, args = [], silent) => __awaiter(void 0, void 0, void 0, function* () {
let stdout = '';
let stderr = '';
const options = {
silent: silent,
ignoreReturnCode: true
};
options.listeners = {
stdout: (data) => {
stdout += data.toString();
},
stderr: (data) => {
stderr += data.toString();
}
};
const returnCode = yield aexec.exec(command, args, options);
return {
success: returnCode === 0,
stdout: stdout.trim(),
stderr: stderr.trim()
};
});

View File

@ -1,176 +1,177 @@
'use strict'; 'use strict';
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) { return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 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 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); } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next()); step((generator = generator.apply(thisArg, _arguments || [])).next());
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.deploy = void 0; exports.deploy = void 0;
const fs = require("fs"); const fs = require("fs");
const core = require("@actions/core"); const core = require("@actions/core");
const yaml = require("js-yaml"); const yaml = require("js-yaml");
const canaryDeploymentHelper = require("./canary-deployment-helper"); const canaryDeploymentHelper = require("./canary-deployment-helper");
const KubernetesObjectUtility = require("../resource-object-utility"); const KubernetesObjectUtility = require("../resource-object-utility");
const TaskInputParameters = require("../../input-parameters"); const TaskInputParameters = require("../../input-parameters");
const models = require("../../constants"); const models = require("../../constants");
const fileHelper = require("../files-helper"); const fileHelper = require("../files-helper");
const utils = require("../manifest-utilities"); const utils = require("../manifest-utilities");
const KubernetesManifestUtility = require("../manifest-stability-utility"); const KubernetesManifestUtility = require("../manifest-stability-utility");
const KubernetesConstants = require("../../constants"); const KubernetesConstants = require("../../constants");
const string_comparison_1 = require("./../string-comparison"); const string_comparison_1 = require("./../string-comparison");
const pod_canary_deployment_helper_1 = require("./pod-canary-deployment-helper"); const pod_canary_deployment_helper_1 = require("./pod-canary-deployment-helper");
const smi_canary_deployment_helper_1 = require("./smi-canary-deployment-helper"); const smi_canary_deployment_helper_1 = require("./smi-canary-deployment-helper");
const utility_1 = require("../utility"); const utility_1 = require("../utility");
function deploy(kubectl, manifestFilePaths, deploymentStrategy) { function deploy(kubectl, manifestFilePaths, deploymentStrategy) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
// get manifest files // get manifest files
let inputManifestFiles = getManifestFiles(manifestFilePaths); let inputManifestFiles = getManifestFiles(manifestFilePaths);
// artifact substitution // artifact substitution
inputManifestFiles = updateResourceObjects(inputManifestFiles, TaskInputParameters.imagePullSecrets, TaskInputParameters.containers); inputManifestFiles = updateResourceObjects(inputManifestFiles, TaskInputParameters.imagePullSecrets, TaskInputParameters.containers);
// deployment // deployment
const deployedManifestFiles = deployManifests(inputManifestFiles, kubectl, isCanaryDeploymentStrategy(deploymentStrategy)); const deployedManifestFiles = deployManifests(inputManifestFiles, kubectl, isCanaryDeploymentStrategy(deploymentStrategy));
// check manifest stability // check manifest stability
const resourceTypes = KubernetesObjectUtility.getResources(deployedManifestFiles, models.deploymentTypes.concat([KubernetesConstants.DiscoveryAndLoadBalancerResource.service])); const resourceTypes = KubernetesObjectUtility.getResources(deployedManifestFiles, models.deploymentTypes.concat([KubernetesConstants.DiscoveryAndLoadBalancerResource.service]));
yield checkManifestStability(kubectl, resourceTypes); yield checkManifestStability(kubectl, resourceTypes);
// print ingress resources // print ingress resources
const ingressResources = KubernetesObjectUtility.getResources(deployedManifestFiles, [KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress]); const ingressResources = KubernetesObjectUtility.getResources(deployedManifestFiles, [KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress]);
ingressResources.forEach(ingressResource => { ingressResources.forEach(ingressResource => {
kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name); kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name);
}); });
// annotate resources // annotate resources
let allPods; let allPods;
try { try {
allPods = JSON.parse((kubectl.getAllPods()).stdout); allPods = JSON.parse((kubectl.getAllPods()).stdout);
} }
catch (e) { catch (e) {
core.debug("Unable to parse pods; Error: " + e); core.debug("Unable to parse pods; Error: " + e);
} }
annotateAndLabelResources(deployedManifestFiles, kubectl, resourceTypes, allPods); annotateAndLabelResources(deployedManifestFiles, kubectl, resourceTypes, allPods);
}); });
} }
exports.deploy = deploy; exports.deploy = deploy;
function getManifestFiles(manifestFilePaths) { function getManifestFiles(manifestFilePaths) {
const files = utils.getManifestFiles(manifestFilePaths); const files = utils.getManifestFiles(manifestFilePaths);
if (files == null || files.length === 0) { if (files == null || files.length === 0) {
throw new Error(`ManifestFileNotFound : ${manifestFilePaths}`); throw new Error(`ManifestFileNotFound : ${manifestFilePaths}`);
} }
return files; return files;
} }
function deployManifests(files, kubectl, isCanaryDeploymentStrategy) { function deployManifests(files, kubectl, isCanaryDeploymentStrategy) {
let result; let result;
if (isCanaryDeploymentStrategy) { if (isCanaryDeploymentStrategy) {
let canaryDeploymentOutput; let canaryDeploymentOutput;
if (canaryDeploymentHelper.isSMICanaryStrategy()) { if (canaryDeploymentHelper.isSMICanaryStrategy()) {
canaryDeploymentOutput = smi_canary_deployment_helper_1.deploySMICanary(kubectl, files); canaryDeploymentOutput = smi_canary_deployment_helper_1.deploySMICanary(kubectl, files);
} }
else { else {
canaryDeploymentOutput = pod_canary_deployment_helper_1.deployPodCanary(kubectl, files); canaryDeploymentOutput = pod_canary_deployment_helper_1.deployPodCanary(kubectl, files);
} }
result = canaryDeploymentOutput.result; result = canaryDeploymentOutput.result;
files = canaryDeploymentOutput.newFilePaths; files = canaryDeploymentOutput.newFilePaths;
} }
else { else {
if (canaryDeploymentHelper.isSMICanaryStrategy()) { if (canaryDeploymentHelper.isSMICanaryStrategy()) {
const updatedManifests = appendStableVersionLabelToResource(files, kubectl); const updatedManifests = appendStableVersionLabelToResource(files, kubectl);
result = kubectl.apply(updatedManifests, TaskInputParameters.forceDeployment); result = kubectl.apply(updatedManifests, TaskInputParameters.forceDeployment);
} }
else { else {
result = kubectl.apply(files, TaskInputParameters.forceDeployment); result = kubectl.apply(files, TaskInputParameters.forceDeployment);
} }
} }
utility_1.checkForErrors([result]); utility_1.checkForErrors([result]);
return files; return files;
} }
function appendStableVersionLabelToResource(files, kubectl) { function appendStableVersionLabelToResource(files, kubectl) {
const manifestFiles = []; const manifestFiles = [];
const newObjectsList = []; const newObjectsList = [];
files.forEach((filePath) => { files.forEach((filePath) => {
const fileContents = fs.readFileSync(filePath); const fileContents = fs.readFileSync(filePath);
yaml.safeLoadAll(fileContents, function (inputObject) { yaml.safeLoadAll(fileContents, function (inputObject) {
const kind = inputObject.kind; const kind = inputObject.kind;
if (KubernetesObjectUtility.isDeploymentEntity(kind)) { if (KubernetesObjectUtility.isDeploymentEntity(kind)) {
const updatedObject = canaryDeploymentHelper.markResourceAsStable(inputObject); const updatedObject = canaryDeploymentHelper.markResourceAsStable(inputObject);
newObjectsList.push(updatedObject); newObjectsList.push(updatedObject);
} }
else { else {
manifestFiles.push(filePath); manifestFiles.push(filePath);
} }
}); });
}); });
const updatedManifestFiles = fileHelper.writeObjectsToFile(newObjectsList); const updatedManifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
manifestFiles.push(...updatedManifestFiles); manifestFiles.push(...updatedManifestFiles);
return manifestFiles; return manifestFiles;
} }
function checkManifestStability(kubectl, resources) { function checkManifestStability(kubectl, resources) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
yield KubernetesManifestUtility.checkManifestStability(kubectl, resources); yield KubernetesManifestUtility.checkManifestStability(kubectl, resources);
}); });
} }
function annotateAndLabelResources(files, kubectl, resourceTypes, allPods) { function annotateAndLabelResources(files, kubectl, resourceTypes, allPods) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const workflowFilePath = yield utility_1.getWorkflowFilePath(TaskInputParameters.githubToken); const workflowFilePath = yield utility_1.getWorkflowFilePath(TaskInputParameters.githubToken);
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath); const filePathsConfig = yield utility_1.getFilePathsConfigs();
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath); const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
labelResources(files, kubectl, annotationKeyLabel); annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, filePathsConfig);
}); labelResources(files, kubectl, annotationKeyLabel);
} });
function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey, workflowFilePath) { }
const annotateResults = []; function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey, workflowFilePath, filePathsConfig) {
const lastSuccessSha = utility_1.getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey); const annotateResults = [];
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath); const lastSuccessSha = utility_1.getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey);
annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, annotationKeyValStr)); let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath, filePathsConfig);
annotateResults.push(kubectl.annotateFiles(files, annotationKeyValStr)); annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, annotationKeyValStr));
resourceTypes.forEach(resource => { annotateResults.push(kubectl.annotateFiles(files, annotationKeyValStr));
if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) { resourceTypes.forEach(resource => {
utility_1.annotateChildPods(kubectl, resource.type, resource.name, annotationKeyValStr, allPods) if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) {
.forEach(execResult => annotateResults.push(execResult)); 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) { }
let workflowName = process.env.GITHUB_WORKFLOW; function labelResources(files, kubectl, label) {
workflowName = workflowName.startsWith('.github/workflows/') ? let workflowName = process.env.GITHUB_WORKFLOW;
workflowName.replace(".github/workflows/", "") : workflowName; workflowName = workflowName.startsWith('.github/workflows/') ?
const labels = [`workflowFriendlyName=${workflowName}`, `workflow=${label}`]; workflowName.replace(".github/workflows/", "") : workflowName;
utility_1.checkForErrors([kubectl.labelFiles(files, labels)], true); const labels = [`workflowFriendlyName=${workflowName}`, `workflow=${label}`];
} utility_1.checkForErrors([kubectl.labelFiles(files, labels)], true);
function updateResourceObjects(filePaths, imagePullSecrets, containers) { }
const newObjectsList = []; function updateResourceObjects(filePaths, imagePullSecrets, containers) {
const updateResourceObject = (inputObject) => { const newObjectsList = [];
if (!!imagePullSecrets && imagePullSecrets.length > 0) { const updateResourceObject = (inputObject) => {
KubernetesObjectUtility.updateImagePullSecrets(inputObject, imagePullSecrets, false); if (!!imagePullSecrets && imagePullSecrets.length > 0) {
} KubernetesObjectUtility.updateImagePullSecrets(inputObject, imagePullSecrets, false);
if (!!containers && containers.length > 0) { }
KubernetesObjectUtility.updateImageDetails(inputObject, containers); if (!!containers && containers.length > 0) {
} KubernetesObjectUtility.updateImageDetails(inputObject, containers);
}; }
filePaths.forEach((filePath) => { };
const fileContents = fs.readFileSync(filePath).toString(); filePaths.forEach((filePath) => {
yaml.safeLoadAll(fileContents, function (inputObject) { const fileContents = fs.readFileSync(filePath).toString();
if (inputObject && inputObject.kind) { yaml.safeLoadAll(fileContents, function (inputObject) {
const kind = inputObject.kind; if (inputObject && inputObject.kind) {
if (KubernetesObjectUtility.isWorkloadEntity(kind)) { const kind = inputObject.kind;
updateResourceObject(inputObject); if (KubernetesObjectUtility.isWorkloadEntity(kind)) {
} updateResourceObject(inputObject);
else if (string_comparison_1.isEqual(kind, 'list', string_comparison_1.StringComparer.OrdinalIgnoreCase)) { }
let items = inputObject.items; else if (string_comparison_1.isEqual(kind, 'list', string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
if (items.length > 0) { let items = inputObject.items;
items.forEach((item) => updateResourceObject(item)); if (items.length > 0) {
} items.forEach((item) => updateResourceObject(item));
} }
newObjectsList.push(inputObject); }
} newObjectsList.push(inputObject);
}); }
}); });
core.debug('New K8s objects after adding imagePullSecrets are :' + JSON.stringify(newObjectsList)); });
const newFilePaths = fileHelper.writeObjectsToFile(newObjectsList); core.debug('New K8s objects after adding imagePullSecrets are :' + JSON.stringify(newObjectsList));
return newFilePaths; const newFilePaths = fileHelper.writeObjectsToFile(newObjectsList);
} return newFilePaths;
function isCanaryDeploymentStrategy(deploymentStrategy) { }
return deploymentStrategy != null && deploymentStrategy.toUpperCase() === canaryDeploymentHelper.CANARY_DEPLOYMENT_STRATEGY.toUpperCase(); function isCanaryDeploymentStrategy(deploymentStrategy) {
} return deploymentStrategy != null && deploymentStrategy.toUpperCase() === canaryDeploymentHelper.CANARY_DEPLOYMENT_STRATEGY.toUpperCase();
}

View File

@ -1,151 +1,214 @@
"use strict"; "use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) { return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 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 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); } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next()); step((generator = generator.apply(thisArg, _arguments || [])).next());
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.annotateChildPods = exports.getWorkflowFilePath = exports.getLastSuccessfulRunSha = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0; exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.getFilePathsConfigs = 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");
const httpClient_1 = require("./httpClient"); const httpClient_1 = require("./httpClient");
function getExecutableExtension() { const exec = require("./exec");
if (os.type().match(/^Win/)) { const inputParams = require("../input-parameters");
return '.exe'; function getExecutableExtension() {
} if (os.type().match(/^Win/)) {
return ''; return '.exe';
} }
exports.getExecutableExtension = getExecutableExtension; return '';
function isEqual(str1, str2, ignoreCase) { }
if (str1 == null && str2 == null) { exports.getExecutableExtension = getExecutableExtension;
return true; function isEqual(str1, str2, ignoreCase) {
} if (str1 == null && str2 == null) {
if (str1 == null || str2 == null) { return true;
return false; }
} if (str1 == null || str2 == null) {
if (ignoreCase) { return false;
return str1.toUpperCase() === str2.toUpperCase(); }
} if (ignoreCase) {
else { return str1.toUpperCase() === str2.toUpperCase();
return str1 === str2; }
} else {
} return str1 === str2;
exports.isEqual = isEqual; }
function checkForErrors(execResults, warnIfError) { }
if (execResults.length !== 0) { exports.isEqual = isEqual;
let stderr = ''; function checkForErrors(execResults, warnIfError) {
execResults.forEach(result => { if (execResults.length !== 0) {
if (result && result.stderr) { let stderr = '';
if (result.code !== 0) { execResults.forEach(result => {
stderr += result.stderr + '\n'; if (result && result.stderr) {
} if (result.code !== 0) {
else { stderr += result.stderr + '\n';
core.warning(result.stderr); }
} else {
} core.warning(result.stderr);
}); }
if (stderr.length > 0) { }
if (warnIfError) { });
core.warning(stderr.trim()); if (stderr.length > 0) {
} if (warnIfError) {
else { core.warning(stderr.trim());
throw new Error(stderr.trim()); }
} else {
} throw new Error(stderr.trim());
} }
} }
exports.checkForErrors = checkForErrors; }
function getLastSuccessfulRunSha(kubectl, namespaceName, annotationKey) { }
try { exports.checkForErrors = checkForErrors;
const result = kubectl.getResource('namespace', namespaceName); function getLastSuccessfulRunSha(kubectl, namespaceName, annotationKey) {
if (result) { try {
if (result.stderr) { const result = kubectl.getResource('namespace', namespaceName);
core.warning(`${result.stderr}`); if (result) {
return process.env.GITHUB_SHA; if (result.stderr) {
} core.warning(`${result.stderr}`);
else if (result.stdout) { return process.env.GITHUB_SHA;
const annotationsSet = JSON.parse(result.stdout).metadata.annotations; }
if (annotationsSet && annotationsSet[annotationKey]) { else if (result.stdout) {
return JSON.parse(annotationsSet[annotationKey].replace(/'/g, '"')).commit; const annotationsSet = JSON.parse(result.stdout).metadata.annotations;
} if (annotationsSet && annotationsSet[annotationKey]) {
else { return JSON.parse(annotationsSet[annotationKey].replace(/'/g, '"')).commit;
return 'NA'; }
} else {
} return 'NA';
} }
} }
catch (ex) { }
core.warning(`Failed to get commits from cluster. ${JSON.stringify(ex)}`); }
return ''; catch (ex) {
} core.warning(`Failed to get commits from cluster. ${JSON.stringify(ex)}`);
} return '';
exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha; }
function getWorkflowFilePath(githubToken) { }
return __awaiter(this, void 0, void 0, function* () { exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha;
let workflowFilePath = process.env.GITHUB_WORKFLOW; function getWorkflowFilePath(githubToken) {
if (!workflowFilePath.startsWith('.github/workflows/')) { return __awaiter(this, void 0, void 0, function* () {
const githubClient = new githubClient_1.GitHubClient(process.env.GITHUB_REPOSITORY, githubToken); let workflowFilePath = process.env.GITHUB_WORKFLOW;
const response = yield githubClient.getWorkflows(); if (!workflowFilePath.startsWith('.github/workflows/')) {
if (response) { const githubClient = new githubClient_1.GitHubClient(process.env.GITHUB_REPOSITORY, githubToken);
if (response.statusCode == httpClient_1.StatusCodes.OK const response = yield githubClient.getWorkflows();
&& response.body if (response) {
&& response.body.total_count) { if (response.statusCode == httpClient_1.StatusCodes.OK
if (response.body.total_count > 0) { && response.body
for (let workflow of response.body.workflows) { && response.body.total_count) {
if (process.env.GITHUB_WORKFLOW === workflow.name) { if (response.body.total_count > 0) {
workflowFilePath = workflow.path; for (let workflow of response.body.workflows) {
break; if (process.env.GITHUB_WORKFLOW === workflow.name) {
} workflowFilePath = workflow.path;
} break;
} }
} }
else if (response.statusCode != httpClient_1.StatusCodes.OK) { }
core.debug(`An error occured while getting list of workflows on the repo. Statuscode: ${response.statusCode}, StatusMessage: ${response.statusMessage}`); }
} else if (response.statusCode != httpClient_1.StatusCodes.OK) {
} core.debug(`An error occured while getting list of workflows on the repo. Statuscode: ${response.statusCode}, StatusMessage: ${response.statusMessage}`);
else { }
core.warning(`Failed to get response from workflow list API`); }
} else {
} core.warning(`Failed to get response from workflow list API`);
return Promise.resolve(workflowFilePath); }
}); }
} return Promise.resolve(workflowFilePath);
exports.getWorkflowFilePath = getWorkflowFilePath; });
function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyValStr, allPods) { }
const commandExecutionResults = []; exports.getWorkflowFilePath = getWorkflowFilePath;
let owner = resourceName; function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyValStr, allPods) {
if (resourceType.toLowerCase().indexOf('deployment') > -1) { const commandExecutionResults = [];
owner = kubectl.getNewReplicaSet(resourceName); let owner = resourceName;
} if (resourceType.toLowerCase().indexOf('deployment') > -1) {
if (allPods && allPods.items && allPods.items.length > 0) { owner = kubectl.getNewReplicaSet(resourceName);
allPods.items.forEach((pod) => { }
const owners = pod.metadata.ownerReferences; if (allPods && allPods.items && allPods.items.length > 0) {
if (owners) { allPods.items.forEach((pod) => {
owners.forEach(ownerRef => { const owners = pod.metadata.ownerReferences;
if (ownerRef.name === owner) { if (owners) {
commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, annotationKeyValStr)); owners.forEach(ownerRef => {
} if (ownerRef.name === owner) {
}); commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, annotationKeyValStr));
} }
}); });
} }
return commandExecutionResults; });
} }
exports.annotateChildPods = annotateChildPods; return commandExecutionResults;
function sleep(timeout) { }
return new Promise(resolve => setTimeout(resolve, timeout)); exports.annotateChildPods = annotateChildPods;
} function getFilePathsConfigs() {
exports.sleep = sleep; return __awaiter(this, void 0, void 0, function* () {
function getRandomInt(max) { let filePathsConfig = {};
return Math.floor(Math.random() * Math.floor(max)); const BUILD_CONFIG_KEY = 'buildConfigs';
} const MANIFEST_PATHS_KEY = 'manifestFilePaths';
exports.getRandomInt = getRandomInt; const HELM_CHART_KEY = 'helmChartFilePaths';
function getCurrentTime() { const DOCKERFILE_PATH_LABEL_KEY = 'dockerfile-path';
return new Date().getTime(); const DOCKERFILE_PATH_KEY = 'dockerfilePath';
} const CONTAINER_REG_KEY = 'containerRegistryServer';
exports.getCurrentTime = getCurrentTime; let inputManifestFiles = inputParams.manifests;
filePathsConfig[MANIFEST_PATHS_KEY] = inputManifestFiles || '';
let helmChartPath = process.env.HELM_CHART_PATH || '';
filePathsConfig[HELM_CHART_KEY] = helmChartPath;
//Fetch labels from each image
let imageToBuildConfigMap = {};
let imageNames = core.getInput('images').split('\n');
for (const image of imageNames) {
let args = [image];
let resultObj;
let buildConfigMap = {};
let containerRegistryName = image.toString().split('/')[0];
try {
let usrname = process.env.CR_USERNAME || null;
let pwd = process.env.CR_PASSWORD || null;
if (pwd && usrname) {
let loginArgs = [containerRegistryName, '--username', usrname, '--password', pwd];
yield exec.exec('docker login ', loginArgs, true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(`docker login failed with: ${res.stderr.match(/(.*)\s*$/)[0]}`);
}
});
}
yield exec.exec('docker pull ', args, true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(`docker images pull failed with: ${res.stderr.match(/(.*)\s*$/)[0]}`);
}
});
yield exec.exec('docker inspect --type=image', args, true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(`docker inspect call failed with: ${res.stderr.match(/(.*)\s*$/)[0]}`);
}
resultObj = JSON.parse(res.stdout)[0];
});
}
catch (ex) {
core.warning(`Failed to get dockerfile paths for image ${image.toString()} | ` + ex);
}
if (resultObj != null && resultObj.Config != null && resultObj.Config.Labels != null) {
if (resultObj.Config.Labels[DOCKERFILE_PATH_LABEL_KEY] != null) {
buildConfigMap[DOCKERFILE_PATH_KEY] = resultObj.Config.Labels[DOCKERFILE_PATH_LABEL_KEY];
}
//Add CR server name to build config
buildConfigMap[CONTAINER_REG_KEY] = containerRegistryName;
imageToBuildConfigMap[resultObj.Id] = buildConfigMap;
}
}
filePathsConfig[BUILD_CONFIG_KEY] = imageToBuildConfigMap;
return Promise.resolve(filePathsConfig);
});
}
exports.getFilePathsConfigs = getFilePathsConfigs;
function sleep(timeout) {
return new Promise(resolve => setTimeout(resolve, timeout));
}
exports.sleep = sleep;
function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
exports.getRandomInt = getRandomInt;
function getCurrentTime() {
return new Date().getTime();
}
exports.getCurrentTime = getCurrentTime;

15873
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,16 +8,16 @@
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
"@actions/tool-cache": "^1.0.0",
"@actions/io": "^1.0.0",
"@actions/core": "^1.2.6", "@actions/core": "^1.2.6",
"@actions/exec": "^1.0.0", "@actions/exec": "^1.0.4",
"@actions/io": "^1.0.0",
"@actions/tool-cache": "^1.0.0",
"js-yaml": "3.13.1" "js-yaml": "3.13.1"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^25.2.2",
"@types/node": "^12.0.10", "@types/node": "^12.0.10",
"jest": "^25.0.0", "jest": "^25.0.0",
"@types/jest": "^25.2.2",
"ts-jest": "^25.5.1", "ts-jest": "^25.5.1",
"typescript": "3.9.5" "typescript": "3.9.5"
} }

View File

@ -25,7 +25,7 @@ export const deploymentTypes: string[] = ['deployment', 'replicaset', 'daemonset
export const workloadTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob']; export const workloadTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
export const workloadTypesWithRolloutStatus: string[] = ['deployment', 'daemonset', 'statefulset']; export const workloadTypesWithRolloutStatus: string[] = ['deployment', 'daemonset', 'statefulset'];
export function getWorkflowAnnotationsJson(lastSuccessRunSha: string, workflowFilePath: string): string { export function getWorkflowAnnotationsJson(lastSuccessRunSha: string, workflowFilePath: string, filePathConfigs: any): string {
return `{` return `{`
+ `'run': '${process.env.GITHUB_RUN_ID}',` + `'run': '${process.env.GITHUB_RUN_ID}',`
+ `'repository': '${process.env.GITHUB_REPOSITORY}',` + `'repository': '${process.env.GITHUB_REPOSITORY}',`
@ -38,6 +38,7 @@ export function getWorkflowAnnotationsJson(lastSuccessRunSha: string, workflowFi
+ `'lastSuccessRunCommit': '${lastSuccessRunSha}',` + `'lastSuccessRunCommit': '${lastSuccessRunSha}',`
+ `'branch': '${process.env.GITHUB_REF}',` + `'branch': '${process.env.GITHUB_REF}',`
+ `'deployTimestamp': '${Date.now()}',` + `'deployTimestamp': '${Date.now()}',`
+ `'filePathConfigs': '${JSON.stringify(filePathConfigs)}',`
+ `'provider': 'GitHub'` + `'provider': 'GitHub'`
+ `}`; + `}`;
} }

34
src/utilities/exec.ts Normal file
View File

@ -0,0 +1,34 @@
import * as aexec from '@actions/exec';
import {ExecOptions} from '@actions/exec';
export interface ExecResult {
success: boolean;
stdout: string;
stderr: string;
}
export const exec = async (command: string, args: string[] = [], silent?: boolean): Promise<ExecResult> => {
let stdout: string = '';
let stderr: string = '';
const options: ExecOptions = {
silent: silent,
ignoreReturnCode: true
};
options.listeners = {
stdout: (data: Buffer) => {
stdout += data.toString();
},
stderr: (data: Buffer) => {
stderr += data.toString();
}
};
const returnCode: number = await aexec.exec(command, args, options);
return {
success: returnCode === 0,
stdout: stdout.trim(),
stderr: stderr.trim()
};
};

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, getWorkflowFilePath, getLastSuccessfulRunSha } from "../utility"; import { checkForErrors, annotateChildPods, getWorkflowFilePath, getLastSuccessfulRunSha, getFilePathsConfigs } from "../utility";
export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], deploymentStrategy: string) { export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], deploymentStrategy: string) {
@ -114,15 +114,16 @@ async function checkManifestStability(kubectl: Kubectl, resources: Resource[]):
async function annotateAndLabelResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any) { async function annotateAndLabelResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any) {
const workflowFilePath = await getWorkflowFilePath(TaskInputParameters.githubToken); const workflowFilePath = await getWorkflowFilePath(TaskInputParameters.githubToken);
const filePathsConfig = await getFilePathsConfigs();
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath); const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath); annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, filePathsConfig);
labelResources(files, kubectl, annotationKeyLabel); 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, filePathsConfig: string) {
const annotateResults: IExecSyncResult[] = []; const annotateResults: IExecSyncResult[] = [];
const lastSuccessSha = getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey); const lastSuccessSha = getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey);
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath); let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath, filePathsConfig);
annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, annotationKeyValStr)); annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, annotationKeyValStr));
annotateResults.push(kubectl.annotateFiles(files, annotationKeyValStr)); annotateResults.push(kubectl.annotateFiles(files, annotationKeyValStr));
resourceTypes.forEach(resource => { resourceTypes.forEach(resource => {

View File

@ -4,6 +4,8 @@ import { IExecSyncResult } from './tool-runner';
import { Kubectl } from '../kubectl-object-model'; import { Kubectl } from '../kubectl-object-model';
import { GitHubClient } from '../githubClient'; import { GitHubClient } from '../githubClient';
import { StatusCodes } from "./httpClient"; import { StatusCodes } from "./httpClient";
import * as exec from "./exec";
import * as inputParams from "../input-parameters";
export function getExecutableExtension(): string { export function getExecutableExtension(): string {
if (os.type().match(/^Win/)) { if (os.type().match(/^Win/)) {
@ -127,6 +129,78 @@ export function annotateChildPods(kubectl: Kubectl, resourceType: string, resour
return commandExecutionResults; return commandExecutionResults;
} }
export async function getFilePathsConfigs(): Promise<any> {
let filePathsConfig: any = {};
const BUILD_CONFIG_KEY = 'buildConfigs';
const MANIFEST_PATHS_KEY = 'manifestFilePaths';
const HELM_CHART_KEY = 'helmChartFilePaths';
const DOCKERFILE_PATH_LABEL_KEY = 'dockerfile-path';
const DOCKERFILE_PATH_KEY = 'dockerfilePath';
const CONTAINER_REG_KEY = 'containerRegistryServer';
let inputManifestFiles = inputParams.manifests;
filePathsConfig[MANIFEST_PATHS_KEY] = inputManifestFiles || '';
let helmChartPath = process.env.HELM_CHART_PATH || '';
filePathsConfig[HELM_CHART_KEY] = helmChartPath;
//Fetch labels from each image
let imageToBuildConfigMap: any = {};
let imageNames = core.getInput('images').split('\n');
for(const image of imageNames){
let args: string[] = [image];
let resultObj: any;
let buildConfigMap : any = {};
let containerRegistryName = image.toString().split('/')[0];
try{
let usrname = process.env.CR_USERNAME || null;
let pwd = process.env.CR_PASSWORD || null;
if(pwd && usrname)
{
let loginArgs: string[] = [containerRegistryName, '--username', usrname, '--password', pwd];
await exec.exec('docker login ', loginArgs, true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(`docker login failed with: ${res.stderr.match(/(.*)\s*$/)![0]}`);
}
});
}
await exec.exec('docker pull ', args, true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(`docker images pull failed with: ${res.stderr.match(/(.*)\s*$/)![0]}`);
}
});
await exec.exec('docker inspect --type=image', args, true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(`docker inspect call failed with: ${res.stderr.match(/(.*)\s*$/)![0]}`);
}
resultObj = JSON.parse(res.stdout)[0];
});
}
catch (ex) {
core.warning(`Failed to get dockerfile paths for image ${image.toString()} | ` + ex);
}
if(resultObj != null && resultObj.Config != null && resultObj.Config.Labels != null ){
if(resultObj.Config.Labels[DOCKERFILE_PATH_LABEL_KEY] !=null){
buildConfigMap[DOCKERFILE_PATH_KEY] = resultObj.Config.Labels[DOCKERFILE_PATH_LABEL_KEY];
}
//Add CR server name to build config
buildConfigMap[CONTAINER_REG_KEY] = containerRegistryName;
imageToBuildConfigMap[resultObj.Id] = buildConfigMap;
}
}
filePathsConfig[BUILD_CONFIG_KEY] = imageToBuildConfigMap;
return Promise.resolve(filePathsConfig);
}
export function sleep(timeout: number) { export function sleep(timeout: number) {
return new Promise(resolve => setTimeout(resolve, timeout)); return new Promise(resolve => setTimeout(resolve, timeout));
} }