mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-04-15 02:52:20 +08:00
New traceability fields annotation changes - design changes (#89)
* New design changes
This commit is contained in:
parent
068b665bf3
commit
d7d5d3429f
@ -293,8 +293,8 @@ test("deployment - deploy() - deploy force flag on", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("deployment - deploy() - Annotate & label resources", async () => {
|
test("deployment - deploy() - Annotate & label resources", async () => {
|
||||||
let filepathConfigs = { manifestsPaths :['manifests/deployment.yaml'], helmChartPaths : [], dockerfilePaths :{}} ;
|
let deploymentConfig = { manifestFilePaths :['manifests/deployment.yaml'], helmChartFilePaths : [], dockerfilePaths :{}} ;
|
||||||
let annotationKeyValStr = getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW) + '=' + getWorkflowAnnotationsJson('currentCommit', '.github/workflows/workflow.yml', filepathConfigs);
|
let annotationKeyValStr = getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW) + '=' + getWorkflowAnnotationsJson('currentCommit', '.github/workflows/workflow.yml', deploymentConfig);
|
||||||
const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true);
|
const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true);
|
||||||
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
||||||
const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true);
|
const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true);
|
||||||
@ -302,7 +302,7 @@ test("deployment - deploy() - Annotate & label resources", async () => {
|
|||||||
const fileHelperMock = mocked(fileHelper, true);
|
const fileHelperMock = mocked(fileHelper, true);
|
||||||
fileHelperMock.writeObjectsToFile = jest.fn().mockReturnValue(["~/Deployment_testapp_currentTimestamp"]);
|
fileHelperMock.writeObjectsToFile = jest.fn().mockReturnValue(["~/Deployment_testapp_currentTimestamp"]);
|
||||||
jest.spyOn(utility, 'getWorkflowFilePath').mockImplementation(() => Promise.resolve(process.env.GITHUB_WORKFLOW));
|
jest.spyOn(utility, 'getWorkflowFilePath').mockImplementation(() => Promise.resolve(process.env.GITHUB_WORKFLOW));
|
||||||
jest.spyOn(utility, 'getFilePathsConfigs').mockImplementation(()=> Promise.resolve(filepathConfigs));
|
jest.spyOn(utility, 'getDeploymentConfig').mockImplementation(()=> Promise.resolve(deploymentConfig));
|
||||||
|
|
||||||
const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any;
|
const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any;
|
||||||
kubeCtl.apply = jest.fn().mockReturnValue("");
|
kubeCtl.apply = jest.fn().mockReturnValue("");
|
||||||
@ -323,8 +323,8 @@ test("deployment - deploy() - Annotate & label resources", async () => {
|
|||||||
|
|
||||||
test("deployment - deploy() - Annotate & label resources for a new workflow", async () => {
|
test("deployment - deploy() - Annotate & label resources for a new workflow", async () => {
|
||||||
process.env.GITHUB_WORKFLOW = '.github/workflows/NewWorkflow.yml';
|
process.env.GITHUB_WORKFLOW = '.github/workflows/NewWorkflow.yml';
|
||||||
let filepathConfigs = { manifestsPaths :['manifests/deployment.yaml'], helmChartPaths : [], dockerfilePaths :{}} ;
|
let deploymentConfig = { manifestFilePaths :['manifests/deployment.yaml'], helmChartFilePaths : [], dockerfilePaths :{}} ;
|
||||||
let annotationKeyValStr = getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW) + '=' + getWorkflowAnnotationsJson('NA', '.github/workflows/NewWorkflow.yml', filepathConfigs);
|
let annotationKeyValStr = getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW) + '=' + getWorkflowAnnotationsJson('NA', '.github/workflows/NewWorkflow.yml', deploymentConfig);
|
||||||
const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true);
|
const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true);
|
||||||
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
||||||
const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true);
|
const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true);
|
||||||
|
|||||||
@ -25,7 +25,7 @@ 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, filePathConfigs) {
|
function getWorkflowAnnotationsJson(lastSuccessRunSha, workflowFilePath, deploymentConfig) {
|
||||||
let annotationObject = {};
|
let annotationObject = {};
|
||||||
annotationObject["run"] = process.env.GITHUB_RUN_ID;
|
annotationObject["run"] = process.env.GITHUB_RUN_ID;
|
||||||
annotationObject["repository"] = process.env.GITHUB_REPOSITORY;
|
annotationObject["repository"] = process.env.GITHUB_REPOSITORY;
|
||||||
@ -38,9 +38,9 @@ function getWorkflowAnnotationsJson(lastSuccessRunSha, workflowFilePath, filePat
|
|||||||
annotationObject["lastSuccessRunCommit"] = lastSuccessRunSha;
|
annotationObject["lastSuccessRunCommit"] = lastSuccessRunSha;
|
||||||
annotationObject["branch"] = process.env.GITHUB_REF;
|
annotationObject["branch"] = process.env.GITHUB_REF;
|
||||||
annotationObject["deployTimestamp"] = Date.now();
|
annotationObject["deployTimestamp"] = Date.now();
|
||||||
annotationObject["dockerfilePaths"] = filePathConfigs.dockerfilePaths;
|
annotationObject["dockerfilePaths"] = deploymentConfig.dockerfilePaths;
|
||||||
annotationObject["manifestsPaths"] = filePathConfigs.manifestFilePaths;
|
annotationObject["manifestsPaths"] = deploymentConfig.manifestFilePaths;
|
||||||
annotationObject["helmChartPaths"] = filePathConfigs.helmChartFilePaths;
|
annotationObject["helmChartPaths"] = deploymentConfig.helmChartFilePaths;
|
||||||
annotationObject["provider"] = "GitHub";
|
annotationObject["provider"] = "GitHub";
|
||||||
return JSON.stringify(annotationObject);
|
return JSON.stringify(annotationObject);
|
||||||
}
|
}
|
||||||
|
|||||||
31
lib/docker-object-model.js
Normal file
31
lib/docker-object-model.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.DockerExec = void 0;
|
||||||
|
const tool_runner_1 = require("./utilities/tool-runner");
|
||||||
|
class DockerExec {
|
||||||
|
constructor(dockerPath) {
|
||||||
|
this.dockerPath = dockerPath;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
pull(image, args, silent) {
|
||||||
|
args = ['pull', image, ...args];
|
||||||
|
let result = this.execute(args, silent);
|
||||||
|
if (result.stderr != '' && result.code != 0) {
|
||||||
|
throw new Error(`docker images pull failed with: ${result.error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inspect(image, args, silent) {
|
||||||
|
args = ['inspect', image, ...args];
|
||||||
|
let result = this.execute(args, silent);
|
||||||
|
if (result.stderr != '' && result.code != 0) {
|
||||||
|
throw new Error(`docker inspect call failed with: ${result.error}`);
|
||||||
|
}
|
||||||
|
return result.stdout;
|
||||||
|
}
|
||||||
|
execute(args, silent) {
|
||||||
|
const command = new tool_runner_1.ToolRunner(this.dockerPath);
|
||||||
|
command.arg(args);
|
||||||
|
return command.execSync({ silent: !!silent });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.DockerExec = DockerExec;
|
||||||
@ -1,35 +0,0 @@
|
|||||||
"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()
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@ -113,16 +113,16 @@ function 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 filePathsConfig = yield utility_1.getFilePathsConfigs();
|
const deploymentConfig = yield utility_1.getDeploymentConfig();
|
||||||
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
|
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
|
||||||
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, filePathsConfig);
|
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, deploymentConfig);
|
||||||
labelResources(files, kubectl, annotationKeyLabel);
|
labelResources(files, kubectl, annotationKeyLabel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey, workflowFilePath, filePathsConfig) {
|
function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey, workflowFilePath, deploymentConfig) {
|
||||||
const annotateResults = [];
|
const annotateResults = [];
|
||||||
const lastSuccessSha = utility_1.getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey);
|
const lastSuccessSha = utility_1.getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey);
|
||||||
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath, filePathsConfig);
|
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath, deploymentConfig);
|
||||||
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 => {
|
||||||
|
|||||||
@ -9,13 +9,14 @@ 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.getFilePathsConfigs = exports.annotateChildPods = exports.getWorkflowFilePath = exports.getLastSuccessfulRunSha = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0;
|
exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.getDeploymentConfig = 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");
|
||||||
const exec = require("./exec");
|
|
||||||
const inputParams = require("../input-parameters");
|
const inputParams = require("../input-parameters");
|
||||||
|
const docker_object_model_1 = require("../docker-object-model");
|
||||||
|
const io = require("@actions/io");
|
||||||
function getExecutableExtension() {
|
function getExecutableExtension() {
|
||||||
if (os.type().match(/^Win/)) {
|
if (os.type().match(/^Win/)) {
|
||||||
return '.exe';
|
return '.exe';
|
||||||
@ -139,81 +140,36 @@ function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyVal
|
|||||||
return commandExecutionResults;
|
return commandExecutionResults;
|
||||||
}
|
}
|
||||||
exports.annotateChildPods = annotateChildPods;
|
exports.annotateChildPods = annotateChildPods;
|
||||||
function getFilePathsConfigs() {
|
function getDeploymentConfig() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let filePathsConfig = {};
|
const inputManifestFiles = inputParams.manifests || [];
|
||||||
const MANIFEST_PATHS_KEY = 'manifestFilePaths';
|
const helmChartPaths = (process.env.HELM_CHART_PATHS && process.env.HELM_CHART_PATHS.split('\n').filter(path => path != "")) || [];
|
||||||
const HELM_CHART_KEY = 'helmChartFilePaths';
|
const imageNames = inputParams.containers || [];
|
||||||
const DOCKERFILE_PATH_KEY = 'dockerfilePaths';
|
|
||||||
const DOCKERFILE_PATH_LABEL_KEY = 'dockerfile-path';
|
|
||||||
let inputManifestFiles = inputParams.manifests || [];
|
|
||||||
filePathsConfig[MANIFEST_PATHS_KEY] = inputManifestFiles;
|
|
||||||
let helmChartPaths = (process.env.HELM_CHART_PATHS && process.env.HELM_CHART_PATHS.split('\n').filter(path => path != "")) || [];
|
|
||||||
filePathsConfig[HELM_CHART_KEY] = helmChartPaths;
|
|
||||||
//Parsing dockerfile paths for images
|
|
||||||
let imageNames = core.getInput('images').split('\n');
|
|
||||||
let imageDockerfilePathMap = {};
|
let imageDockerfilePathMap = {};
|
||||||
let pathKey, pathVal;
|
//Fetching from image label if available
|
||||||
//Fetching from env var if available
|
|
||||||
let dockerfilePathsList = (process.env.DOCKERFILE_PATHS && process.env.DOCKERFILE_PATHS.split('\n')) || [];
|
|
||||||
dockerfilePathsList.forEach(path => {
|
|
||||||
if (path) {
|
|
||||||
pathKey = path.split(' ')[0];
|
|
||||||
pathVal = path.split(' ')[1];
|
|
||||||
imageDockerfilePathMap[pathKey] = pathVal;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
//Fetching from image lable if available
|
|
||||||
for (const image of imageNames) {
|
for (const image of imageNames) {
|
||||||
let args = [image];
|
let imageConfig, imageInspectResult;
|
||||||
let resultObj;
|
|
||||||
let containerRegistryName = image;
|
|
||||||
try {
|
try {
|
||||||
let usrname = process.env.CR_USERNAME || null;
|
yield checkDockerPath();
|
||||||
let pwd = process.env.CR_PASSWORD || null;
|
var dockerExec = new docker_object_model_1.DockerExec('docker');
|
||||||
if (pwd && usrname) {
|
dockerExec.pull(image, [], true);
|
||||||
let loginArgs = [containerRegistryName, '--username', usrname, '--password', pwd];
|
imageInspectResult = dockerExec.inspect(image, [], true);
|
||||||
yield exec.exec('docker login ', loginArgs, true).then(res => {
|
imageConfig = JSON.parse(imageInspectResult)[0];
|
||||||
if (res.stderr != '' && !res.success) {
|
imageDockerfilePathMap[image] = getDockerfilePath(imageConfig);
|
||||||
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]}`);
|
|
||||||
}
|
|
||||||
if (res.stdout) {
|
|
||||||
resultObj = JSON.parse(res.stdout);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.warning(`Failed to get dockerfile paths for image ${image.toString()} | ` + ex);
|
core.warning(`Failed to get dockerfile path for image ${image.toString()} | ` + ex);
|
||||||
}
|
|
||||||
if (resultObj) {
|
|
||||||
resultObj = resultObj[0];
|
|
||||||
if ((resultObj.Config) && (resultObj.Config.Labels) && (resultObj.Config.Labels[DOCKERFILE_PATH_LABEL_KEY])) {
|
|
||||||
pathVal = resultObj.Config.Labels[DOCKERFILE_PATH_LABEL_KEY];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pathVal = 'Not available';
|
|
||||||
}
|
|
||||||
if (!imageDockerfilePathMap[image]) { //If (image : someVal) does not exist from env var parsing then add
|
|
||||||
imageDockerfilePathMap[image] = pathVal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filePathsConfig[DOCKERFILE_PATH_KEY] = imageDockerfilePathMap;
|
const deploymentConfig = {
|
||||||
return Promise.resolve(filePathsConfig);
|
manifestFilePaths: inputManifestFiles,
|
||||||
|
helmChartFilePaths: helmChartPaths,
|
||||||
|
dockerfilePaths: imageDockerfilePathMap
|
||||||
|
};
|
||||||
|
return Promise.resolve(deploymentConfig);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.getFilePathsConfigs = getFilePathsConfigs;
|
exports.getDeploymentConfig = getDeploymentConfig;
|
||||||
function sleep(timeout) {
|
function sleep(timeout) {
|
||||||
return new Promise(resolve => setTimeout(resolve, timeout));
|
return new Promise(resolve => setTimeout(resolve, timeout));
|
||||||
}
|
}
|
||||||
@ -226,3 +182,32 @@ function getCurrentTime() {
|
|||||||
return new Date().getTime();
|
return new Date().getTime();
|
||||||
}
|
}
|
||||||
exports.getCurrentTime = getCurrentTime;
|
exports.getCurrentTime = getCurrentTime;
|
||||||
|
function checkDockerPath() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let dockerPath = yield io.which('docker', false);
|
||||||
|
if (!dockerPath) {
|
||||||
|
throw new Error('Docker is not installed.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getDockerfilePath(imageConfig) {
|
||||||
|
const DOCKERFILE_PATH_LABEL_KEY = 'dockerfile-path';
|
||||||
|
const ref = process.env.GITHUB_REF && process.env.GITHUB_REF.replace('refs/heads/', '').replace('refs/tags/', '');
|
||||||
|
let pathLabel, pathLink, pathValue = '';
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
import { DeploymentConfig } from "./utilities/utility";
|
||||||
|
|
||||||
export class KubernetesWorkload {
|
export class KubernetesWorkload {
|
||||||
public static pod: string = 'Pod';
|
public static pod: string = 'Pod';
|
||||||
@ -25,7 +26,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, filePathConfigs: any): string {
|
export function getWorkflowAnnotationsJson(lastSuccessRunSha: string, workflowFilePath: string, deploymentConfig: DeploymentConfig): string {
|
||||||
let annotationObject: any = {};
|
let annotationObject: any = {};
|
||||||
annotationObject["run"] = process.env.GITHUB_RUN_ID;
|
annotationObject["run"] = process.env.GITHUB_RUN_ID;
|
||||||
annotationObject["repository"] = process.env.GITHUB_REPOSITORY;
|
annotationObject["repository"] = process.env.GITHUB_REPOSITORY;
|
||||||
@ -38,9 +39,9 @@ export function getWorkflowAnnotationsJson(lastSuccessRunSha: string, workflowFi
|
|||||||
annotationObject["lastSuccessRunCommit"] = lastSuccessRunSha;
|
annotationObject["lastSuccessRunCommit"] = lastSuccessRunSha;
|
||||||
annotationObject["branch"] = process.env.GITHUB_REF;
|
annotationObject["branch"] = process.env.GITHUB_REF;
|
||||||
annotationObject["deployTimestamp"] = Date.now();
|
annotationObject["deployTimestamp"] = Date.now();
|
||||||
annotationObject["dockerfilePaths"] = filePathConfigs.dockerfilePaths;
|
annotationObject["dockerfilePaths"] = deploymentConfig.dockerfilePaths;
|
||||||
annotationObject["manifestsPaths"] = filePathConfigs.manifestFilePaths
|
annotationObject["manifestsPaths"] = deploymentConfig.manifestFilePaths
|
||||||
annotationObject["helmChartPaths"] = filePathConfigs.helmChartFilePaths;
|
annotationObject["helmChartPaths"] = deploymentConfig.helmChartFilePaths;
|
||||||
annotationObject["provider"] = "GitHub";
|
annotationObject["provider"] = "GitHub";
|
||||||
|
|
||||||
return JSON.stringify(annotationObject);
|
return JSON.stringify(annotationObject);
|
||||||
|
|||||||
33
src/docker-object-model.ts
Normal file
33
src/docker-object-model.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,33 +0,0 @@
|
|||||||
import * as aexec 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: aexec.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()
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -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, getFilePathsConfigs } from "../utility";
|
import { checkForErrors, annotateChildPods, getWorkflowFilePath, getLastSuccessfulRunSha, getDeploymentConfig, DeploymentConfig } from "../utility";
|
||||||
|
|
||||||
|
|
||||||
export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], deploymentStrategy: string) {
|
export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], deploymentStrategy: string) {
|
||||||
@ -114,16 +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 deploymentConfig = await getDeploymentConfig();
|
||||||
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
|
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
|
||||||
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, filePathsConfig);
|
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, deploymentConfig);
|
||||||
labelResources(files, kubectl, annotationKeyLabel);
|
labelResources(files, kubectl, annotationKeyLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
function annotateResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any, annotationKey: string, workflowFilePath: string, filePathsConfig: string) {
|
function annotateResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any, annotationKey: string, workflowFilePath: string, deploymentConfig: DeploymentConfig) {
|
||||||
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, filePathsConfig);
|
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath, deploymentConfig);
|
||||||
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 => {
|
||||||
|
|||||||
@ -4,8 +4,15 @@ 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";
|
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 {
|
export function getExecutableExtension(): string {
|
||||||
if (os.type().match(/^Win/)) {
|
if (os.type().match(/^Win/)) {
|
||||||
@ -129,93 +136,40 @@ export function annotateChildPods(kubectl: Kubectl, resourceType: string, resour
|
|||||||
return commandExecutionResults;
|
return commandExecutionResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFilePathsConfigs(): Promise<any> {
|
export async function getDeploymentConfig(): Promise<DeploymentConfig> {
|
||||||
|
|
||||||
let filePathsConfig: any = {};
|
const inputManifestFiles = inputParams.manifests || [];
|
||||||
const MANIFEST_PATHS_KEY = 'manifestFilePaths';
|
const helmChartPaths = (process.env.HELM_CHART_PATHS && process.env.HELM_CHART_PATHS.split('\n').filter(path => path != "")) || [];
|
||||||
const HELM_CHART_KEY = 'helmChartFilePaths';
|
const imageNames = inputParams.containers || [];
|
||||||
const DOCKERFILE_PATH_KEY = 'dockerfilePaths';
|
let imageDockerfilePathMap: { [id: string]: string; } = {};
|
||||||
const DOCKERFILE_PATH_LABEL_KEY = 'dockerfile-path';
|
|
||||||
|
|
||||||
let inputManifestFiles = inputParams.manifests || [];
|
//Fetching from image label if available
|
||||||
filePathsConfig[MANIFEST_PATHS_KEY] = inputManifestFiles;
|
for (const image of imageNames) {
|
||||||
|
let imageConfig: any, imageInspectResult: string;
|
||||||
|
|
||||||
let helmChartPaths = (process.env.HELM_CHART_PATHS && process.env.HELM_CHART_PATHS.split('\n').filter(path => path != "")) || [];
|
try {
|
||||||
filePathsConfig[HELM_CHART_KEY] = helmChartPaths;
|
await checkDockerPath();
|
||||||
|
var dockerExec: DockerExec = new DockerExec('docker');
|
||||||
//Parsing dockerfile paths for images
|
dockerExec.pull(image, [], true);
|
||||||
let imageNames = core.getInput('images').split('\n');
|
imageInspectResult = dockerExec.inspect(image, [], true);
|
||||||
let imageDockerfilePathMap: any = {};
|
imageConfig = JSON.parse(imageInspectResult)[0];
|
||||||
let pathKey: any, pathVal: any;
|
imageDockerfilePathMap[image] = getDockerfilePath(imageConfig);
|
||||||
|
|
||||||
//Fetching from env var if available
|
|
||||||
let dockerfilePathsList: any[] = (process.env.DOCKERFILE_PATHS && process.env.DOCKERFILE_PATHS.split('\n')) || [];
|
|
||||||
dockerfilePathsList.forEach(path => {
|
|
||||||
if(path){
|
|
||||||
pathKey = path.split(' ')[0];
|
|
||||||
pathVal = path.split(' ')[1];
|
|
||||||
imageDockerfilePathMap[pathKey] = pathVal;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
//Fetching from image lable if available
|
|
||||||
for(const image of imageNames){
|
|
||||||
let args: string[] = [image];
|
|
||||||
let resultObj: any;
|
|
||||||
let containerRegistryName = image;
|
|
||||||
|
|
||||||
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]}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(res.stdout){
|
|
||||||
resultObj = JSON.parse(res.stdout);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.warning(`Failed to get dockerfile paths for image ${image.toString()} | ` + ex);
|
core.warning(`Failed to get dockerfile path for image ${image.toString()} | ` + ex);
|
||||||
}
|
|
||||||
|
|
||||||
if(resultObj){
|
|
||||||
resultObj = resultObj[0];
|
|
||||||
if((resultObj.Config) && (resultObj.Config.Labels) && (resultObj.Config.Labels[DOCKERFILE_PATH_LABEL_KEY])){
|
|
||||||
pathVal = resultObj.Config.Labels[DOCKERFILE_PATH_LABEL_KEY];
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
pathVal = 'Not available';
|
|
||||||
}
|
|
||||||
if(!imageDockerfilePathMap[image]){ //If (image : someVal) does not exist from env var parsing then add
|
|
||||||
imageDockerfilePathMap[image] = pathVal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filePathsConfig[DOCKERFILE_PATH_KEY] = imageDockerfilePathMap;
|
const deploymentConfig = <DeploymentConfig>{
|
||||||
|
manifestFilePaths: inputManifestFiles,
|
||||||
|
helmChartFilePaths: helmChartPaths,
|
||||||
|
dockerfilePaths: imageDockerfilePathMap
|
||||||
|
};
|
||||||
|
|
||||||
return Promise.resolve(filePathsConfig);
|
return Promise.resolve(deploymentConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function sleep(timeout: number) {
|
export function sleep(timeout: number) {
|
||||||
return new Promise(resolve => setTimeout(resolve, timeout));
|
return new Promise(resolve => setTimeout(resolve, timeout));
|
||||||
}
|
}
|
||||||
@ -227,3 +181,33 @@ export function getRandomInt(max: number) {
|
|||||||
export function getCurrentTime(): number {
|
export function getCurrentTime(): number {
|
||||||
return new Date().getTime();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user