Merge pull request #46 from Azure/tauhid621/optionalForceFlag

force flag added
This commit is contained in:
Tauhid Anjum 2020-06-20 00:45:15 +05:30 коммит произвёл GitHub
Родитель f1898e0618 6773ba4167
Коммит c9b54fdae2
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
27 изменённых файлов: 95 добавлений и 17 удалений

Просмотреть файл

@ -71,6 +71,10 @@ Following are the key capabilities of this action:
<td><code>kubectl-version</code><br/>Kubectl version</td>
<td>(Optional) Version of kubectl client to be used for deploying the manifest to the cluster. If this input is left unspecified, latest version is used.</td>
</tr>
<tr>
<td><code>force</code><br/>Force</td>
<td>(Optional) Deploy when a previous deployment already exists. If true then '--force' argument is added to the apply command. Using '--force' argument is not recommended in production.</td>
</tr>
</table>
## Examples YAML snippets

Просмотреть файл

@ -6,6 +6,8 @@ import * as deployment from '../src/utilities/strategy-helpers/deployment-helper
import * as fs from 'fs';
import * as io from '@actions/io';
import * as toolCache from '@actions/tool-cache';
import * as utility from '../src/utilities/utility';
import * as inputParam from '../src/input-parameters';
import { Kubectl, Resource } from '../src/kubectl-object-model';
@ -16,6 +18,8 @@ var path = require('path');
const coreMock = mocked(core, true);
const ioMock = mocked(io, true);
const utilityMock = mocked(utility, true);
const inputParamMock = mocked(inputParam, true);
const toolCacheMock = mocked(toolCache, true);
const fileUtility = mocked(fs, true);
@ -194,3 +198,37 @@ test("deployment - deploy() - Invokes with manifestfiles", async () => {
expect(readFileSpy).toBeCalledWith("manifests/deployment.yaml");
expect(kubeCtl.getResource).toBeCalledWith("ingress", "AppName");
});
test("run() - deploy force flag on", async () => {
const kubectlVersion = 'v1.18.0'
//Mocks
coreMock.getInput = jest.fn().mockImplementation((name) => {
if (name == 'manifests') {
return 'manifests/deployment.yaml';
}
if (name == 'action') {
return 'deploy';
}
if (name == 'strategy') {
return undefined;
}
if (name == 'force') {
return 'true';
}
return kubectlVersion;
});
inputParamMock.forceDeployment = true;
coreMock.setFailed = jest.fn();
toolCacheMock.find = jest.fn().mockReturnValue('validPath');
toolCacheMock.downloadTool = jest.fn().mockReturnValue('downloadpath');
toolCacheMock.cacheFile = jest.fn().mockReturnValue('cachepath');
fileUtility.chmodSync = jest.fn();
utilityMock.checkForErrors = jest.fn();
const deploySpy = jest.spyOn(Kubectl.prototype, 'apply').mockImplementation();
//Invoke and assert
await expect(action.run()).resolves.not.toThrow();
expect(deploySpy).toBeCalledWith(expect.anything(), true);
deploySpy.mockRestore();
});

Просмотреть файл

@ -41,6 +41,10 @@ inputs:
description: 'deploy/promote/reject'
required: true
default: 'deploy'
force:
description: 'Deploy when a previous deployment already exists. If true then --force argument is added to the apply command'
required: false
default: false
branding:
color: 'green' # optional, decorates the entry in the GitHub Marketplace

Просмотреть файл

@ -8,6 +8,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");

Просмотреть файл

@ -8,6 +8,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");

Просмотреть файл

@ -1,5 +1,6 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.workloadTypesWithRolloutStatus = exports.workloadTypes = exports.deploymentTypes = exports.ServiceTypes = exports.DiscoveryAndLoadBalancerResource = exports.KubernetesWorkload = void 0;
class KubernetesWorkload {
}
exports.KubernetesWorkload = KubernetesWorkload;

Просмотреть файл

@ -1,5 +1,6 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.forceDeployment = 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');
@ -10,6 +11,7 @@ exports.deploymentStrategy = core.getInput('strategy');
exports.trafficSplitMethod = core.getInput('traffic-split-method');
exports.baselineAndCanaryReplicas = core.getInput('baseline-and-canary-replicas');
exports.args = core.getInput('arguments');
exports.forceDeployment = core.getInput('force').toLowerCase() == 'true';
if (!exports.namespace) {
core.debug('Namespace was not supplied; using "default" namespace instead.');
exports.namespace = 'default';

Просмотреть файл

@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Kubectl = void 0;
const tool_runner_1 = require("./utilities/tool-runner");
class Kubectl {
constructor(kubectlPath, namespace, ignoreSSLErrors) {
@ -21,8 +22,13 @@ class Kubectl {
this.namespace = 'default';
}
}
apply(configurationPaths) {
return this.execute(['apply', '-f', this.createInlineArray(configurationPaths)]);
apply(configurationPaths, force) {
let applyArgs = ['apply', '-f', this.createInlineArray(configurationPaths)];
if (!!force) {
console.log("force flag is on, deployment will continue even if previous deployment already exists");
applyArgs.push('--force');
}
return this.execute(applyArgs);
}
describe(resourceType, resourceName, silent) {
return this.execute(['describe', resourceType, resourceName], silent);

Просмотреть файл

@ -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");

Просмотреть файл

@ -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");

Просмотреть файл

@ -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");

Просмотреть файл

@ -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");

Просмотреть файл

@ -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");

Просмотреть файл

@ -1,5 +1,6 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.getResources = exports.updateSelectorLabels = exports.updateSpecLabels = exports.updateImageDetails = exports.updateImagePullSecrets = 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");

Просмотреть файл

@ -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");

Просмотреть файл

@ -8,6 +8,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 path = require("path");
const core = require("@actions/core");
@ -67,10 +68,10 @@ function deployManifests(files, kubectl, isCanaryDeploymentStrategy) {
else {
if (canaryDeploymentHelper.isSMICanaryStrategy()) {
const updatedManifests = appendStableVersionLabelToResource(files, kubectl);
result = kubectl.apply(updatedManifests);
result = kubectl.apply(updatedManifests, TaskInputParameters.forceDeployment);
}
else {
result = kubectl.apply(files);
result = kubectl.apply(files, TaskInputParameters.forceDeployment);
}
}
utility_1.checkForErrors([result]);

Просмотреть файл

@ -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");
@ -47,7 +48,7 @@ function deployPodCanary(kubectl, filePaths) {
});
});
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
const result = kubectl.apply(manifestFiles);
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
return { 'result': result, 'newFilePaths': manifestFiles };
}
exports.deployPodCanary = deployPodCanary;

Просмотреть файл

@ -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");
@ -55,7 +56,7 @@ function deploySMICanary(kubectl, filePaths) {
});
});
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
const result = kubectl.apply(manifestFiles);
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
createCanaryService(kubectl, filePaths);
return { 'result': result, 'newFilePaths': manifestFiles };
}
@ -110,7 +111,7 @@ function createCanaryService(kubectl, filePaths) {
});
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
manifestFiles.push(...trafficObjectsList);
const result = kubectl.apply(manifestFiles);
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
utility_1.checkForErrors([result]);
}
function redirectTrafficToCanaryDeployment(kubectl, manifestFilePaths) {
@ -143,7 +144,7 @@ function adjustTraffic(kubectl, manifestFilePaths, stableWeight, canaryWeight) {
if (trafficSplitManifests.length <= 0) {
return;
}
const result = kubectl.apply(trafficSplitManifests);
const result = kubectl.apply(trafficSplitManifests, TaskInputParameters.forceDeployment);
core.debug('serviceObjects:' + serviceObjects.join(',') + ' result:' + result);
utility_1.checkForErrors([result]);
}

Просмотреть файл

@ -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";

Просмотреть файл

@ -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");

Просмотреть файл

@ -1,5 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0;
const os = require("os");
const core = require("@actions/core");
function getExecutableExtension() {

Просмотреть файл

@ -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"
}
}

Просмотреть файл

@ -11,6 +11,7 @@ export const deploymentStrategy: string = core.getInput('strategy');
export const trafficSplitMethod: string = core.getInput('traffic-split-method');
export const baselineAndCanaryReplicas: string = core.getInput('baseline-and-canary-replicas');
export const args: string = core.getInput('arguments');
export const forceDeployment: boolean = core.getInput('force').toLowerCase() == 'true';
if (!namespace) {
core.debug('Namespace was not supplied; using "default" namespace instead.');

Просмотреть файл

@ -20,8 +20,15 @@ export class Kubectl {
}
}
public apply(configurationPaths: string | string[]) {
return this.execute(['apply', '-f', this.createInlineArray(configurationPaths)]);
public apply(configurationPaths: string | string[], force?: boolean) {
let applyArgs: string[] = ['apply', '-f', this.createInlineArray(configurationPaths)];
if (!!force) {
console.log("force flag is on, deployment will continue even if previous deployment already exists");
applyArgs.push('--force');
}
return this.execute(applyArgs);
}
public describe(resourceType: string, resourceName: string, silent?: boolean) {

Просмотреть файл

@ -68,10 +68,10 @@ function deployManifests(files: string[], kubectl: Kubectl, isCanaryDeploymentSt
} else {
if (canaryDeploymentHelper.isSMICanaryStrategy()) {
const updatedManifests = appendStableVersionLabelToResource(files, kubectl);
result = kubectl.apply(updatedManifests);
result = kubectl.apply(updatedManifests, TaskInputParameters.forceDeployment);
}
else {
result = kubectl.apply(files);
result = kubectl.apply(files, TaskInputParameters.forceDeployment);
}
}
checkForErrors([result]);

Просмотреть файл

@ -52,7 +52,7 @@ export function deployPodCanary(kubectl: Kubectl, filePaths: string[]) {
});
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
const result = kubectl.apply(manifestFiles);
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
return { 'result': result, 'newFilePaths': manifestFiles };
}

Просмотреть файл

@ -60,7 +60,7 @@ export function deploySMICanary(kubectl: Kubectl, filePaths: string[]) {
});
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
const result = kubectl.apply(manifestFiles);
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
createCanaryService(kubectl, filePaths);
return { 'result': result, 'newFilePaths': manifestFiles };
}
@ -121,7 +121,7 @@ function createCanaryService(kubectl: Kubectl, filePaths: string[]) {
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
manifestFiles.push(...trafficObjectsList);
const result = kubectl.apply(manifestFiles);
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
checkForErrors([result]);
}
@ -159,7 +159,7 @@ function adjustTraffic(kubectl: Kubectl, manifestFilePaths: string[], stableWeig
return;
}
const result = kubectl.apply(trafficSplitManifests);
const result = kubectl.apply(trafficSplitManifests, TaskInputParameters.forceDeployment);
core.debug('serviceObjects:' + serviceObjects.join(',') + ' result:' + result);
checkForErrors([result]);
}