diff --git a/lib/actions/promote.js b/lib/actions/promote.js index 2bdf58c9..50e3fd05 100644 --- a/lib/actions/promote.js +++ b/lib/actions/promote.js @@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.promote = void 0; const core = require("@actions/core"); const deploymentHelper = require("../utilities/strategy-helpers/deployment-helper"); const canaryDeploymentHelper = require("../utilities/strategy-helpers/canary-deployment-helper"); diff --git a/lib/actions/reject.js b/lib/actions/reject.js index d3177ef6..cb0d73ee 100644 --- a/lib/actions/reject.js +++ b/lib/actions/reject.js @@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.reject = void 0; const core = require("@actions/core"); const canaryDeploymentHelper = require("../utilities/strategy-helpers/canary-deployment-helper"); const SMICanaryDeploymentHelper = require("../utilities/strategy-helpers/smi-canary-deployment-helper"); diff --git a/lib/constants.js b/lib/constants.js index 50919389..5367fd9c 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.workflowAnnotations = exports.workloadTypesWithRolloutStatus = exports.workloadTypes = exports.deploymentTypes = exports.ServiceTypes = exports.DiscoveryAndLoadBalancerResource = exports.KubernetesWorkload = void 0; class KubernetesWorkload { } exports.KubernetesWorkload = KubernetesWorkload; @@ -24,3 +25,12 @@ ServiceTypes.clusterIP = 'ClusterIP'; exports.deploymentTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset']; exports.workloadTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob']; exports.workloadTypesWithRolloutStatus = ['deployment', 'daemonset', 'statefulset']; +exports.workflowAnnotations = [ + `run=${process.env['GITHUB_RUN_ID']}`, + `workflow="${process.env['GITHUB_WORKFLOW']}"`, + `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']}`, + `branch=${process.env['GITHUB_REF']}` +]; diff --git a/lib/input-parameters.js b/lib/input-parameters.js index 63869312..c4903363 100644 --- a/lib/input-parameters.js +++ b/lib/input-parameters.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.args = exports.baselineAndCanaryReplicas = exports.trafficSplitMethod = exports.deploymentStrategy = exports.canaryPercentage = exports.manifests = exports.imagePullSecrets = exports.containers = exports.namespace = void 0; const core = require("@actions/core"); exports.namespace = core.getInput('namespace'); exports.containers = core.getInput('images').split('\n'); diff --git a/lib/kubectl-object-model.js b/lib/kubectl-object-model.js index 9effab10..688a4f72 100644 --- a/lib/kubectl-object-model.js +++ b/lib/kubectl-object-model.js @@ -1,14 +1,6 @@ "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.Kubectl = void 0; const tool_runner_1 = require("./utilities/tool-runner"); class Kubectl { constructor(kubectlPath, namespace, ignoreSSLErrors) { @@ -28,19 +20,34 @@ class Kubectl { return this.execute(['describe', resourceType, resourceName], silent); } getNewReplicaSet(deployment) { - return __awaiter(this, void 0, void 0, function* () { - let newReplicaSet = ''; - const result = yield this.describe('deployment', deployment, true); - if (result && result.stdout) { - const stdout = result.stdout.split('\n'); - stdout.forEach((line) => { - if (!!line && line.toLowerCase().indexOf('newreplicaset') > -1) { - newReplicaSet = line.substr(14).trim().split(' ')[0]; - } - }); - } - return newReplicaSet; - }); + let newReplicaSet = ''; + const result = this.describe('deployment', deployment, true); + if (result && result.stdout) { + const stdout = result.stdout.split('\n'); + stdout.forEach((line) => { + if (!!line && line.toLowerCase().indexOf('newreplicaset') > -1) { + newReplicaSet = line.substr(14).trim().split(' ')[0]; + } + }); + } + return newReplicaSet; + } + annotate(resourceType, resourceName, annotations, overwrite) { + let args = ['annotate', resourceType, resourceName]; + args = args.concat(annotations); + if (!!overwrite) { + args.push(`--overwrite`); + } + return this.execute(args); + } + annotateFiles(files, annotations, overwrite) { + let args = ['annotate']; + args = args.concat(['-f', this.createInlineArray(files)]); + args = args.concat(annotations); + if (!!overwrite) { + args.push(`--overwrite`); + } + return this.execute(args); } getAllPods() { return this.execute(['get', 'pods', '-o', 'json'], true); diff --git a/lib/run.js b/lib/run.js index 369a0efd..09207bf6 100644 --- a/lib/run.js +++ b/lib/run.js @@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.run = void 0; const core = require("@actions/core"); const io = require("@actions/io"); const path = require("path"); diff --git a/lib/utilities/files-helper.js b/lib/utilities/files-helper.js index b7cd2754..4b9ecc81 100644 --- a/lib/utilities/files-helper.js +++ b/lib/utilities/files-helper.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.writeManifestToFile = exports.writeObjectsToFile = exports.assertFileExists = exports.ensureDirExists = exports.getNewUserDirPath = exports.getTempDirectory = void 0; const fs = require("fs"); const path = require("path"); const core = require("@actions/core"); diff --git a/lib/utilities/kubectl-util.js b/lib/utilities/kubectl-util.js index 464e088e..a5bbcb63 100644 --- a/lib/utilities/kubectl-util.js +++ b/lib/utilities/kubectl-util.js @@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.getTrafficSplitAPIVersion = exports.downloadKubectl = exports.getStableKubectlVersion = exports.getkubectlDownloadURL = void 0; const core = require("@actions/core"); const fs = require("fs"); const os = require("os"); diff --git a/lib/utilities/manifest-stability-utility.js b/lib/utilities/manifest-stability-utility.js index c5cd5013..778219a7 100644 --- a/lib/utilities/manifest-stability-utility.js +++ b/lib/utilities/manifest-stability-utility.js @@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.checkPodStatus = exports.checkManifestStability = void 0; const core = require("@actions/core"); const utils = require("./utility"); const KubernetesConstants = require("../constants"); diff --git a/lib/utilities/manifest-utilities.js b/lib/utilities/manifest-utilities.js index 04f29ff8..0237cdc4 100644 --- a/lib/utilities/manifest-utilities.js +++ b/lib/utilities/manifest-utilities.js @@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.isWorkloadEntity = exports.updateImagePullSecrets = exports.updateContainerImagesInManifestFiles = exports.substituteImageNameInSpecFile = exports.getDeleteCmdArgs = exports.createKubectlArgs = exports.getKubectl = exports.getManifestFiles = void 0; const core = require("@actions/core"); const kubectlutility = require("./kubectl-util"); const io = require("@actions/io"); diff --git a/lib/utilities/resource-object-utility.js b/lib/utilities/resource-object-utility.js index 14c7f740..70236845 100644 --- a/lib/utilities/resource-object-utility.js +++ b/lib/utilities/resource-object-utility.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.getResources = exports.updateSelectorLabels = exports.updateSpecLabels = exports.updateImageDetails = exports.updateImagePullSecrets = exports.updateObjectAnnotations = exports.updateObjectLabels = exports.getReplicaCount = exports.isServiceEntity = exports.isWorkloadEntity = exports.isDeploymentEntity = void 0; const fs = require("fs"); const core = require("@actions/core"); const yaml = require("js-yaml"); @@ -69,6 +70,31 @@ function updateObjectLabels(inputObject, newLabels, override) { } } exports.updateObjectLabels = updateObjectLabels; +function updateObjectAnnotations(inputObject, newAnnotations, override) { + if (!inputObject) { + throw ('NullInputObject'); + } + if (!inputObject.metadata) { + throw ('NullInputObjectMetadata'); + } + if (!newAnnotations) { + return; + } + if (override) { + inputObject.metadata.annotations = newAnnotations; + } + else { + let existingAnnotations = inputObject.metadata.annotations; + if (!existingAnnotations) { + existingAnnotations = new Map(); + } + Object.keys(newAnnotations).forEach(function (key) { + existingAnnotations[key] = newAnnotations[key]; + }); + inputObject.metadata.annotations = existingAnnotations; + } +} +exports.updateObjectAnnotations = updateObjectAnnotations; function updateImagePullSecrets(inputObject, newImagePullSecrets, override) { if (!inputObject || !inputObject.spec || !newImagePullSecrets) { return; diff --git a/lib/utilities/strategy-helpers/canary-deployment-helper.js b/lib/utilities/strategy-helpers/canary-deployment-helper.js index d8c5cb91..d4518630 100644 --- a/lib/utilities/strategy-helpers/canary-deployment-helper.js +++ b/lib/utilities/strategy-helpers/canary-deployment-helper.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.getStableResourceName = exports.getBaselineResourceName = exports.getCanaryResourceName = exports.isSMICanaryStrategy = exports.isCanaryDeploymentStrategy = exports.fetchResource = exports.fetchCanaryResource = exports.getNewCanaryResource = exports.getNewBaselineResource = exports.getStableResource = exports.isResourceMarkedAsStable = exports.markResourceAsStable = exports.deleteCanaryDeployment = exports.STABLE_LABEL_VALUE = exports.STABLE_SUFFIX = exports.CANARY_LABEL_VALUE = exports.BASELINE_LABEL_VALUE = exports.CANARY_VERSION_LABEL = exports.TRAFFIC_SPLIT_STRATEGY = exports.CANARY_DEPLOYMENT_STRATEGY = void 0; const fs = require("fs"); const yaml = require("js-yaml"); const core = require("@actions/core"); @@ -154,6 +155,7 @@ function addCanaryLabelsAndAnnotations(inputObject, type) { const newLabels = new Map(); newLabels[exports.CANARY_VERSION_LABEL] = type; helper.updateObjectLabels(inputObject, newLabels, false); + helper.updateObjectAnnotations(inputObject, newLabels, false); helper.updateSelectorLabels(inputObject, newLabels, false); if (!helper.isServiceEntity(inputObject.kind)) { helper.updateSpecLabels(inputObject, newLabels, false); diff --git a/lib/utilities/strategy-helpers/deployment-helper.js b/lib/utilities/strategy-helpers/deployment-helper.js index a880bd9c..f94c16ce 100644 --- a/lib/utilities/strategy-helpers/deployment-helper.js +++ b/lib/utilities/strategy-helpers/deployment-helper.js @@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); +exports.deploy = void 0; const fs = require("fs"); const core = require("@actions/core"); const yaml = require("js-yaml"); @@ -40,6 +41,15 @@ function deploy(kubectl, manifestFilePaths, deploymentStrategy) { ingressResources.forEach(ingressResource => { kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name); }); + // annotate resources + let allPods; + try { + allPods = JSON.parse((kubectl.getAllPods()).stdout); + } + catch (e) { + core.debug("Unable to parse pods; Error: " + e); + } + annotateResources(deployedManifestFiles, kubectl, resourceTypes, allPods); }); } exports.deploy = deploy; @@ -100,6 +110,17 @@ function checkManifestStability(kubectl, resources) { yield KubernetesManifestUtility.checkManifestStability(kubectl, resources); }); } +function annotateResources(files, kubectl, resourceTypes, allPods) { + const annotateResults = []; + annotateResults.push(kubectl.annotateFiles(files, models.workflowAnnotations, true)); + resourceTypes.forEach(resource => { + if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) { + utility_1.annotateChildPods(kubectl, resource.type, resource.name, allPods) + .forEach(execResult => annotateResults.push(execResult)); + } + }); + utility_1.checkForErrors(annotateResults, true); +} function updateResourceObjects(filePaths, imagePullSecrets, containers) { const newObjectsList = []; const updateResourceObject = (inputObject) => { diff --git a/lib/utilities/strategy-helpers/pod-canary-deployment-helper.js b/lib/utilities/strategy-helpers/pod-canary-deployment-helper.js index 3a66581a..4e0ae883 100644 --- a/lib/utilities/strategy-helpers/pod-canary-deployment-helper.js +++ b/lib/utilities/strategy-helpers/pod-canary-deployment-helper.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.deployPodCanary = void 0; const core = require("@actions/core"); const fs = require("fs"); const yaml = require("js-yaml"); diff --git a/lib/utilities/strategy-helpers/smi-canary-deployment-helper.js b/lib/utilities/strategy-helpers/smi-canary-deployment-helper.js index 4a871824..fd90abcb 100644 --- a/lib/utilities/strategy-helpers/smi-canary-deployment-helper.js +++ b/lib/utilities/strategy-helpers/smi-canary-deployment-helper.js @@ -1,5 +1,6 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); +exports.redirectTrafficToStableDeployment = exports.redirectTrafficToCanaryDeployment = exports.deploySMICanary = void 0; const core = require("@actions/core"); const fs = require("fs"); const yaml = require("js-yaml"); diff --git a/lib/utilities/string-comparison.js b/lib/utilities/string-comparison.js index 5edaa9aa..9dd1b0a1 100644 --- a/lib/utilities/string-comparison.js +++ b/lib/utilities/string-comparison.js @@ -1,5 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.isEqual = exports.StringComparer = void 0; var StringComparer; (function (StringComparer) { StringComparer[StringComparer["Ordinal"] = 0] = "Ordinal"; diff --git a/lib/utilities/tool-runner.js b/lib/utilities/tool-runner.js index cd46a5f5..e1ea302e 100644 --- a/lib/utilities/tool-runner.js +++ b/lib/utilities/tool-runner.js @@ -1,5 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.ToolRunner = void 0; const os = require("os"); const events = require("events"); const child = require("child_process"); diff --git a/lib/utilities/utility.js b/lib/utilities/utility.js index 35b79737..7d68af81 100644 --- a/lib/utilities/utility.js +++ b/lib/utilities/utility.js @@ -1,7 +1,9 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.annotateChildPods = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0; const os = require("os"); const core = require("@actions/core"); +const constants_1 = require("../constants"); function getExecutableExtension() { if (os.type().match(/^Win/)) { return '.exe'; @@ -48,6 +50,27 @@ function checkForErrors(execResults, warnIfError) { } } exports.checkForErrors = checkForErrors; +function annotateChildPods(kubectl, resourceType, resourceName, allPods) { + const commandExecutionResults = []; + let owner = resourceName; + if (resourceType.toLowerCase().indexOf('deployment') > -1) { + owner = kubectl.getNewReplicaSet(resourceName); + } + if (!!allPods && !!allPods.items && allPods.items.length > 0) { + allPods.items.forEach((pod) => { + const owners = pod.metadata.ownerReferences; + if (!!owners) { + owners.forEach(ownerRef => { + if (ownerRef.name === owner) { + commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, constants_1.workflowAnnotations, true)); + } + }); + } + }); + } + return commandExecutionResults; +} +exports.annotateChildPods = annotateChildPods; function sleep(timeout) { return new Promise(resolve => setTimeout(resolve, timeout)); } diff --git a/package.json b/package.json index 66c46c66..e7090263 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,6 @@ "jest": "^25.0.0", "@types/jest": "^25.2.2", "ts-jest": "^25.5.1", - "typescript": "^3.9.2" + "typescript": "3.9.5" } } diff --git a/src/constants.ts b/src/constants.ts index 4d523e56..5aa6f845 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -24,3 +24,13 @@ export class ServiceTypes { export const deploymentTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset']; export const workloadTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob']; export const workloadTypesWithRolloutStatus: string[] = ['deployment', 'daemonset', 'statefulset']; + +export let workflowAnnotations = [ + `run=${process.env['GITHUB_RUN_ID']}`, + `workflow="${process.env['GITHUB_WORKFLOW']}"`, + `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']}`, + `branch=${process.env['GITHUB_REF']}` +]; \ No newline at end of file diff --git a/src/kubectl-object-model.ts b/src/kubectl-object-model.ts index 62ec9ced..2dcffa0a 100644 --- a/src/kubectl-object-model.ts +++ b/src/kubectl-object-model.ts @@ -1,4 +1,5 @@ -import { ToolRunner, IExecOptions } from "./utilities/tool-runner"; +import { ToolRunner, IExecOptions, IExecSyncResult } from "./utilities/tool-runner"; +import * as core from '@actions/core'; export interface Resource { name: string; @@ -20,17 +21,17 @@ export class Kubectl { } } - public apply(configurationPaths: string | string[]) { + public apply(configurationPaths: string | string[]): IExecSyncResult { return this.execute(['apply', '-f', this.createInlineArray(configurationPaths)]); } - public describe(resourceType: string, resourceName: string, silent?: boolean) { + public describe(resourceType: string, resourceName: string, silent?: boolean): IExecSyncResult { return this.execute(['describe', resourceType, resourceName], silent); } - public async getNewReplicaSet(deployment: string) { + public getNewReplicaSet(deployment: string) { let newReplicaSet = ''; - const result = await this.describe('deployment', deployment, true); + const result = this.describe('deployment', deployment, true); if (result && result.stdout) { const stdout = result.stdout.split('\n'); stdout.forEach((line: string) => { @@ -43,19 +44,34 @@ export class Kubectl { return newReplicaSet; } - public getAllPods() { + public annotate(resourceType: string, resourceName: string, annotations: string[], overwrite?: boolean): IExecSyncResult { + let args = ['annotate', resourceType, resourceName]; + args = args.concat(annotations); + if (!!overwrite) { args.push(`--overwrite`); } + return this.execute(args); + } + + public annotateFiles(files: string | string[], annotations: string[], overwrite?: boolean): IExecSyncResult { + let args = ['annotate']; + args = args.concat(['-f', this.createInlineArray(files)]); + args = args.concat(annotations); + if (!!overwrite) { args.push(`--overwrite`); } + return this.execute(args); + } + + public getAllPods(): IExecSyncResult { return this.execute(['get', 'pods', '-o', 'json'], true); } - public getClusterInfo() { + public getClusterInfo(): IExecSyncResult { return this.execute(['cluster-info'], true); } - public checkRolloutStatus(resourceType: string, name: string) { + public checkRolloutStatus(resourceType: string, name: string): IExecSyncResult { return this.execute(['rollout', 'status', resourceType + '/' + name]); } - public getResource(resourceType: string, name: string) { + public getResource(resourceType: string, name: string): IExecSyncResult { return this.execute(['get', resourceType + '/' + name, '-o', 'json']); } @@ -80,7 +96,7 @@ export class Kubectl { } public executeCommand(customCommand: string, args?: string) { - if(!customCommand) + if (!customCommand) throw new Error('NullCommandForKubectl'); return args ? this.execute([customCommand, args]) : this.execute([customCommand]); } diff --git a/src/utilities/resource-object-utility.ts b/src/utilities/resource-object-utility.ts index 343b7e7c..cae80f4e 100644 --- a/src/utilities/resource-object-utility.ts +++ b/src/utilities/resource-object-utility.ts @@ -81,6 +81,34 @@ export function updateObjectLabels(inputObject: any, newLabels: Map, override: boolean) { + if (!inputObject) { + throw ('NullInputObject'); + } + + if (!inputObject.metadata) { + throw ('NullInputObjectMetadata'); + } + + if (!newAnnotations) { + return; + } + if (override) { + inputObject.metadata.annotations = newAnnotations; + } else { + let existingAnnotations = inputObject.metadata.annotations; + if (!existingAnnotations) { + existingAnnotations = new Map(); + } + + Object.keys(newAnnotations).forEach(function (key) { + existingAnnotations[key] = newAnnotations[key]; + }); + + inputObject.metadata.annotations = existingAnnotations; + } +} + export function updateImagePullSecrets(inputObject: any, newImagePullSecrets: string[], override: boolean) { if (!inputObject || !inputObject.spec || !newImagePullSecrets) { return; diff --git a/src/utilities/strategy-helpers/canary-deployment-helper.ts b/src/utilities/strategy-helpers/canary-deployment-helper.ts index 88fcca1a..541c48c0 100644 --- a/src/utilities/strategy-helpers/canary-deployment-helper.ts +++ b/src/utilities/strategy-helpers/canary-deployment-helper.ts @@ -177,6 +177,7 @@ function addCanaryLabelsAndAnnotations(inputObject: any, type: string) { newLabels[CANARY_VERSION_LABEL] = type; helper.updateObjectLabels(inputObject, newLabels, false); + helper.updateObjectAnnotations(inputObject, newLabels, false); helper.updateSelectorLabels(inputObject, newLabels, false); if (!helper.isServiceEntity(inputObject.kind)) { diff --git a/src/utilities/strategy-helpers/deployment-helper.ts b/src/utilities/strategy-helpers/deployment-helper.ts index 238c4a4b..121dd9d8 100644 --- a/src/utilities/strategy-helpers/deployment-helper.ts +++ b/src/utilities/strategy-helpers/deployment-helper.ts @@ -1,7 +1,6 @@ 'use strict'; import * as fs from 'fs'; -import * as path from 'path'; import * as core from '@actions/core'; import * as yaml from 'js-yaml'; import * as canaryDeploymentHelper from './canary-deployment-helper'; @@ -14,10 +13,11 @@ import * as KubernetesManifestUtility from '../manifest-stability-utility'; import * as KubernetesConstants from '../../constants'; import { Kubectl, Resource } from '../../kubectl-object-model'; import { StringComparer, isEqual } from './../string-comparison'; +import { IExecSyncResult } from '../../utilities/tool-runner'; import { deployPodCanary } from './pod-canary-deployment-helper'; import { deploySMICanary } from './smi-canary-deployment-helper'; -import { checkForErrors } from "../utility"; +import { checkForErrors, annotateChildPods } from "../utility"; export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], deploymentStrategy: string) { @@ -40,6 +40,16 @@ 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 { + allPods = JSON.parse((kubectl.getAllPods()).stdout); + } catch (e) { + core.debug("Unable to parse pods; Error: " + e); + } + + annotateResources(deployedManifestFiles, kubectl, resourceTypes, allPods); } function getManifestFiles(manifestFilePaths: string[]): string[] { @@ -102,6 +112,18 @@ async function checkManifestStability(kubectl: Kubectl, resources: Resource[]): await KubernetesManifestUtility.checkManifestStability(kubectl, resources); } +function annotateResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any) { + const annotateResults: IExecSyncResult[] = []; + annotateResults.push(kubectl.annotateFiles(files, models.workflowAnnotations, true)); + resourceTypes.forEach(resource => { + if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) { + annotateChildPods(kubectl, resource.type, resource.name, allPods) + .forEach(execResult => annotateResults.push(execResult)); + } + }); + checkForErrors(annotateResults, true); +} + function updateResourceObjects(filePaths: string[], imagePullSecrets: string[], containers: string[]): string[] { const newObjectsList = []; const updateResourceObject = (inputObject) => { diff --git a/src/utilities/utility.ts b/src/utilities/utility.ts index 75799ab1..c1605220 100644 --- a/src/utilities/utility.ts +++ b/src/utilities/utility.ts @@ -1,5 +1,8 @@ import * as os from 'os'; import * as core from '@actions/core'; +import { IExecSyncResult } from './tool-runner'; +import { Kubectl } from '../kubectl-object-model'; +import { workflowAnnotations } from '../constants'; export function getExecutableExtension(): string { if (os.type().match(/^Win/)) { @@ -25,7 +28,7 @@ export function isEqual(str1: string, str2: string, ignoreCase?: boolean): boole } } -export function checkForErrors(execResults, warnIfError?: boolean) { +export function checkForErrors(execResults: IExecSyncResult[], warnIfError?: boolean) { if (execResults.length !== 0) { let stderr = ''; execResults.forEach(result => { @@ -47,6 +50,29 @@ export function checkForErrors(execResults, warnIfError?: boolean) { } } +export function annotateChildPods(kubectl: Kubectl, resourceType: string, resourceName: string, allPods): IExecSyncResult[] { + const commandExecutionResults = []; + let owner = resourceName; + if (resourceType.toLowerCase().indexOf('deployment') > -1) { + owner = kubectl.getNewReplicaSet(resourceName); + } + + if (!!allPods && !!allPods.items && allPods.items.length > 0) { + allPods.items.forEach((pod) => { + const owners = pod.metadata.ownerReferences; + if (!!owners) { + owners.forEach(ownerRef => { + if (ownerRef.name === owner) { + commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, workflowAnnotations, true)); + } + }); + } + }); + } + + return commandExecutionResults; +} + export function sleep(timeout: number) { return new Promise(resolve => setTimeout(resolve, timeout)); }