Compare commits

..

2 Commits

Author SHA1 Message Date
Oliver King c86a95c404 Add node modules and compiled JavaScript from main 2022-11-23 21:06:24 +00:00
Oliver King 2cfbf514e9 Merge branch 'releases/v4' into tmp 2022-11-23 21:06:04 +00:00
23 changed files with 455 additions and 624 deletions
+9 -4
View File
@@ -13,15 +13,20 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v2
with: with:
# We must fetch at least the immediate parents so that if this is # We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head. # a pull request then we can checkout the head.
fetch-depth: 2 fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v1
# Override language selection by uncommenting this and choosing your languages # Override language selection by uncommenting this and choosing your languages
# with: # with:
# languages: go, javascript, csharp, python, cpp, java # languages: go, javascript, csharp, python, cpp, java
@@ -29,7 +34,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@v2 uses: github/codeql-action/autobuild@v1
# ️ Command-line programs to run using the OS shell. # ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@@ -43,4 +48,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v1
+1 -2
View File
@@ -11,10 +11,9 @@ on: # rebuild any PRs and main branch changes
jobs: jobs:
build: # make sure build/ci works properly build: # make sure build/ci works properly
name: Run Unit Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v1
- run: | - run: |
npm install npm install
npm test npm test
+9 -9
View File
@@ -128,7 +128,7 @@ Following are the key capabilities of this action:
### Basic deployment (without any deployment strategy) ### Basic deployment (without any deployment strategy)
```yaml ```yaml
- uses: Azure/k8s-deploy@v4 - uses: Azure/k8s-deploy@v3.1
with: with:
namespace: 'myapp' namespace: 'myapp'
manifests: | manifests: |
@@ -162,7 +162,7 @@ Following are the key capabilities of this action:
### Canary deployment without service mesh ### Canary deployment without service mesh
```yaml ```yaml
- uses: Azure/k8s-deploy@v4 - uses: Azure/k8s-deploy@v3.1
with: with:
namespace: 'myapp' namespace: 'myapp'
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}' images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
@@ -181,7 +181,7 @@ Following are the key capabilities of this action:
To promote/reject the canary created by the above snippet, the following YAML snippet could be used: To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
```yaml ```yaml
- uses: Azure/k8s-deploy@v4 - uses: Azure/k8s-deploy@v3.1
with: with:
namespace: 'myapp' namespace: 'myapp'
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}' images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
@@ -199,7 +199,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
### Canary deployment based on Service Mesh Interface ### Canary deployment based on Service Mesh Interface
```yaml ```yaml
- uses: Azure/k8s-deploy@v4 - uses: Azure/k8s-deploy@v3.1
with: with:
namespace: 'myapp' namespace: 'myapp'
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}' images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
@@ -220,7 +220,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
To promote/reject the canary created by the above snippet, the following YAML snippet could be used: To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
```yaml ```yaml
- uses: Azure/k8s-deploy@v4 - uses: Azure/k8s-deploy@v3.1
with: with:
namespace: 'myapp' namespace: 'myapp'
images: 'contoso.azurecr.io/myapp:${{ event.run_id }} ' images: 'contoso.azurecr.io/myapp:${{ event.run_id }} '
@@ -239,7 +239,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
### Blue-Green deployment with different route methods ### Blue-Green deployment with different route methods
```yaml ```yaml
- uses: Azure/k8s-deploy@v4 - uses: Azure/k8s-deploy@v3.1
with: with:
namespace: 'myapp' namespace: 'myapp'
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}' images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
@@ -259,7 +259,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
To promote/reject the green workload created by the above snippet, the following YAML snippet could be used: To promote/reject the green workload created by the above snippet, the following YAML snippet could be used:
```yaml ```yaml
- uses: Azure/k8s-deploy@v4 - uses: Azure/k8s-deploy@v3.1
with: with:
namespace: 'myapp' namespace: 'myapp'
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}' images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
@@ -316,7 +316,7 @@ jobs:
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }} container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
secret-name: demo-k8s-secret secret-name: demo-k8s-secret
- uses: Azure/k8s-deploy@v4 - uses: Azure/k8s-deploy@v3.1
with: with:
action: deploy action: deploy
manifests: | manifests: |
@@ -362,7 +362,7 @@ jobs:
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }} container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
secret-name: demo-k8s-secret secret-name: demo-k8s-secret
- uses: Azure/k8s-deploy@v4 - uses: Azure/k8s-deploy@v3.1
with: with:
action: deploy action: deploy
manifests: | manifests: |
+115 -165
View File
@@ -140,6 +140,7 @@ const file_command_1 = __nccwpck_require__(8466);
const utils_1 = __nccwpck_require__(7369); const utils_1 = __nccwpck_require__(7369);
const os = __importStar(__nccwpck_require__(2037)); const os = __importStar(__nccwpck_require__(2037));
const path = __importStar(__nccwpck_require__(1017)); const path = __importStar(__nccwpck_require__(1017));
const uuid_1 = __nccwpck_require__(487);
const oidc_utils_1 = __nccwpck_require__(7557); const oidc_utils_1 = __nccwpck_require__(7557);
/** /**
* The code to exit an action * The code to exit an action
@@ -169,9 +170,20 @@ function exportVariable(name, val) {
process.env[name] = convertedVal; process.env[name] = convertedVal;
const filePath = process.env['GITHUB_ENV'] || ''; const filePath = process.env['GITHUB_ENV'] || '';
if (filePath) { if (filePath) {
return file_command_1.issueFileCommand('ENV', file_command_1.prepareKeyValueMessage(name, val)); const delimiter = `ghadelimiter_${uuid_1.v4()}`;
// These should realistically never happen, but just in case someone finds a way to exploit uuid generation let's not allow keys or values that contain the delimiter.
if (name.includes(delimiter)) {
throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`);
} }
if (convertedVal.includes(delimiter)) {
throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`);
}
const commandValue = `${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}`;
file_command_1.issueCommand('ENV', commandValue);
}
else {
command_1.issueCommand('set-env', { name }, convertedVal); command_1.issueCommand('set-env', { name }, convertedVal);
}
} }
exports.exportVariable = exportVariable; exports.exportVariable = exportVariable;
/** /**
@@ -189,7 +201,7 @@ exports.setSecret = setSecret;
function addPath(inputPath) { function addPath(inputPath) {
const filePath = process.env['GITHUB_PATH'] || ''; const filePath = process.env['GITHUB_PATH'] || '';
if (filePath) { if (filePath) {
file_command_1.issueFileCommand('PATH', inputPath); file_command_1.issueCommand('PATH', inputPath);
} }
else { else {
command_1.issueCommand('add-path', {}, inputPath); command_1.issueCommand('add-path', {}, inputPath);
@@ -229,10 +241,7 @@ function getMultilineInput(name, options) {
const inputs = getInput(name, options) const inputs = getInput(name, options)
.split('\n') .split('\n')
.filter(x => x !== ''); .filter(x => x !== '');
if (options && options.trimWhitespace === false) {
return inputs; return inputs;
}
return inputs.map(input => input.trim());
} }
exports.getMultilineInput = getMultilineInput; exports.getMultilineInput = getMultilineInput;
/** /**
@@ -265,12 +274,8 @@ exports.getBooleanInput = getBooleanInput;
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
function setOutput(name, value) { function setOutput(name, value) {
const filePath = process.env['GITHUB_OUTPUT'] || '';
if (filePath) {
return file_command_1.issueFileCommand('OUTPUT', file_command_1.prepareKeyValueMessage(name, value));
}
process.stdout.write(os.EOL); process.stdout.write(os.EOL);
command_1.issueCommand('set-output', { name }, utils_1.toCommandValue(value)); command_1.issueCommand('set-output', { name }, value);
} }
exports.setOutput = setOutput; exports.setOutput = setOutput;
/** /**
@@ -399,11 +404,7 @@ exports.group = group;
*/ */
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
function saveState(name, value) { function saveState(name, value) {
const filePath = process.env['GITHUB_STATE'] || ''; command_1.issueCommand('save-state', { name }, value);
if (filePath) {
return file_command_1.issueFileCommand('STATE', file_command_1.prepareKeyValueMessage(name, value));
}
command_1.issueCommand('save-state', { name }, utils_1.toCommandValue(value));
} }
exports.saveState = saveState; exports.saveState = saveState;
/** /**
@@ -469,14 +470,13 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result; return result;
}; };
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.prepareKeyValueMessage = exports.issueFileCommand = void 0; exports.issueCommand = void 0;
// We use any as a valid input type // We use any as a valid input type
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
const fs = __importStar(__nccwpck_require__(7147)); const fs = __importStar(__nccwpck_require__(7147));
const os = __importStar(__nccwpck_require__(2037)); const os = __importStar(__nccwpck_require__(2037));
const uuid_1 = __nccwpck_require__(487);
const utils_1 = __nccwpck_require__(7369); const utils_1 = __nccwpck_require__(7369);
function issueFileCommand(command, message) { function issueCommand(command, message) {
const filePath = process.env[`GITHUB_${command}`]; const filePath = process.env[`GITHUB_${command}`];
if (!filePath) { if (!filePath) {
throw new Error(`Unable to find environment variable for file command ${command}`); throw new Error(`Unable to find environment variable for file command ${command}`);
@@ -488,22 +488,7 @@ function issueFileCommand(command, message) {
encoding: 'utf8' encoding: 'utf8'
}); });
} }
exports.issueFileCommand = issueFileCommand; exports.issueCommand = issueCommand;
function prepareKeyValueMessage(key, value) {
const delimiter = `ghadelimiter_${uuid_1.v4()}`;
const convertedValue = utils_1.toCommandValue(value);
// These should realistically never happen, but just in case someone finds a
// way to exploit uuid generation let's not allow keys or values that contain
// the delimiter.
if (key.includes(delimiter)) {
throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`);
}
if (convertedValue.includes(delimiter)) {
throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`);
}
return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`;
}
exports.prepareKeyValueMessage = prepareKeyValueMessage;
//# sourceMappingURL=file-command.js.map //# sourceMappingURL=file-command.js.map
/***/ }), /***/ }),
@@ -20210,7 +20195,7 @@ function deploy(kubectl, manifestFilePaths, deploymentStrategy) {
KubernetesConstants.DiscoveryAndLoadBalancerResource.INGRESS KubernetesConstants.DiscoveryAndLoadBalancerResource.INGRESS
]); ]);
for (const ingressResource of ingressResources) { for (const ingressResource of ingressResources) {
yield kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.INGRESS, ingressResource.name, false, ingressResource.namespace); yield kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.INGRESS, ingressResource.name);
} }
core.endGroup(); core.endGroup();
// annotate resources // annotate resources
@@ -20593,8 +20578,7 @@ function deleteGreenObjects(kubectl, toDelete) {
const resourcesToDelete = toDelete.map((obj) => { const resourcesToDelete = toDelete.map((obj) => {
return { return {
name: getBlueGreenResourceName(obj.metadata.name, exports.GREEN_SUFFIX), name: getBlueGreenResourceName(obj.metadata.name, exports.GREEN_SUFFIX),
kind: obj.kind, kind: obj.kind
namespace: obj.metadata.namespace
}; };
}); });
core.debug(`deleting green objects: ${JSON.stringify(resourcesToDelete)}`); core.debug(`deleting green objects: ${JSON.stringify(resourcesToDelete)}`);
@@ -20621,24 +20605,28 @@ exports.deleteObjects = deleteObjects;
// other common functions // other common functions
function getManifestObjects(filePaths) { function getManifestObjects(filePaths) {
const deploymentEntityList = []; const deploymentEntityList = [];
const serviceEntityList = [];
const routedServiceEntityList = []; const routedServiceEntityList = [];
const unroutedServiceEntityList = []; const unroutedServiceEntityList = [];
const ingressEntityList = []; const ingressEntityList = [];
const otherEntitiesList = []; const otherEntitiesList = [];
const serviceNameMap = new Map(); const serviceNameMap = new Map();
// Manifest objects per type. All resources should be parsed and
// organized before we can check if services are “routed” or not.
filePaths.forEach((filePath) => { filePaths.forEach((filePath) => {
const fileContents = fs.readFileSync(filePath).toString(); const fileContents = fs.readFileSync(filePath).toString();
yaml.safeLoadAll(fileContents, (inputObject) => { yaml.safeLoadAll(fileContents, (inputObject) => {
if (!!inputObject) { if (!!inputObject) {
const kind = inputObject.kind; const kind = inputObject.kind;
const name = inputObject.metadata.name;
if (kubernetesTypes_1.isDeploymentEntity(kind)) { if (kubernetesTypes_1.isDeploymentEntity(kind)) {
deploymentEntityList.push(inputObject); deploymentEntityList.push(inputObject);
} }
else if (kubernetesTypes_1.isServiceEntity(kind)) { else if (kubernetesTypes_1.isServiceEntity(kind)) {
serviceEntityList.push(inputObject); if (isServiceRouted(inputObject, deploymentEntityList)) {
routedServiceEntityList.push(inputObject);
serviceNameMap.set(name, getBlueGreenResourceName(name, exports.GREEN_SUFFIX));
}
else {
unroutedServiceEntityList.push(inputObject);
}
} }
else if (kubernetesTypes_1.isIngressEntity(kind)) { else if (kubernetesTypes_1.isIngressEntity(kind)) {
ingressEntityList.push(inputObject); ingressEntityList.push(inputObject);
@@ -20649,16 +20637,6 @@ function getManifestObjects(filePaths) {
} }
}); });
}); });
serviceEntityList.forEach((inputObject) => {
if (isServiceRouted(inputObject, deploymentEntityList)) {
const name = inputObject.metadata.name;
routedServiceEntityList.push(inputObject);
serviceNameMap.set(name, getBlueGreenResourceName(name, exports.GREEN_SUFFIX));
}
else {
unroutedServiceEntityList.push(inputObject);
}
});
return { return {
serviceEntityList: routedServiceEntityList, serviceEntityList: routedServiceEntityList,
serviceNameMap: serviceNameMap, serviceNameMap: serviceNameMap,
@@ -20753,9 +20731,9 @@ function isServiceSelectorSubsetOfMatchLabel(serviceSelector, matchLabels) {
return isMatch; return isMatch;
} }
exports.isServiceSelectorSubsetOfMatchLabel = isServiceSelectorSubsetOfMatchLabel; exports.isServiceSelectorSubsetOfMatchLabel = isServiceSelectorSubsetOfMatchLabel;
function fetchResource(kubectl, kind, name, namespace) { function fetchResource(kubectl, kind, name) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const result = yield kubectl.getResource(kind, name, false, namespace); const result = yield kubectl.getResource(kind, name);
if (result == null || !!result.stderr) { if (result == null || !!result.stderr) {
return null; return null;
} }
@@ -20955,16 +20933,16 @@ function isIngressRouted(ingressObject, serviceNameMap) {
} }
exports.isIngressRouted = isIngressRouted; exports.isIngressRouted = isIngressRouted;
function validateIngresses(kubectl, ingressEntityList, serviceNameMap) { function validateIngresses(kubectl, ingressEntityList, serviceNameMap) {
var _a, _b; var _a;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let areValid = true; let areValid = true;
const invalidIngresses = []; const invalidIngresses = [];
for (const inputObject of ingressEntityList) { for (const inputObject of ingressEntityList) {
if (isIngressRouted(inputObject, serviceNameMap)) { if (isIngressRouted(inputObject, serviceNameMap)) {
//querying existing ingress //querying existing ingress
const existingIngress = yield blueGreenHelper_1.fetchResource(kubectl, inputObject.kind, inputObject.metadata.name, (_a = inputObject === null || inputObject === void 0 ? void 0 : inputObject.metadata) === null || _a === void 0 ? void 0 : _a.namespace); const existingIngress = yield blueGreenHelper_1.fetchResource(kubectl, inputObject.kind, inputObject.metadata.name);
const isValid = !!existingIngress && const isValid = !!existingIngress &&
((_b = existingIngress === null || existingIngress === void 0 ? void 0 : existingIngress.metadata) === null || _b === void 0 ? void 0 : _b.labels[blueGreenHelper_1.BLUE_GREEN_VERSION_LABEL]) === ((_a = existingIngress === null || existingIngress === void 0 ? void 0 : existingIngress.metadata) === null || _a === void 0 ? void 0 : _a.labels[blueGreenHelper_1.BLUE_GREEN_VERSION_LABEL]) ===
blueGreenHelper_1.GREEN_LABEL_VALUE; blueGreenHelper_1.GREEN_LABEL_VALUE;
if (!isValid) { if (!isValid) {
core.debug(`Invalid ingress detected (must be in green state): ${JSON.stringify(inputObject)}`); core.debug(`Invalid ingress detected (must be in green state): ${JSON.stringify(inputObject)}`);
@@ -21222,12 +21200,11 @@ function getUpdatedBlueGreenService(inputObject, labelValue) {
} }
exports.getUpdatedBlueGreenService = getUpdatedBlueGreenService; exports.getUpdatedBlueGreenService = getUpdatedBlueGreenService;
function validateServicesState(kubectl, serviceEntityList) { function validateServicesState(kubectl, serviceEntityList) {
var _a;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let areServicesGreen = true; let areServicesGreen = true;
for (const serviceObject of serviceEntityList) { for (const serviceObject of serviceEntityList) {
// finding the existing routed service // finding the existing routed service
const existingService = yield blueGreenHelper_1.fetchResource(kubectl, serviceObject.kind, serviceObject.metadata.name, (_a = serviceObject === null || serviceObject === void 0 ? void 0 : serviceObject.metadata) === null || _a === void 0 ? void 0 : _a.namespace); const existingService = yield blueGreenHelper_1.fetchResource(kubectl, serviceObject.kind, serviceObject.metadata.name);
let isServiceGreen = !!existingService && let isServiceGreen = !!existingService &&
getServiceSpecLabel(existingService) == getServiceSpecLabel(existingService) ==
blueGreenHelper_1.GREEN_LABEL_VALUE; blueGreenHelper_1.GREEN_LABEL_VALUE;
@@ -21348,12 +21325,11 @@ function getGreenSMIServiceResource(inputObject) {
} }
exports.getGreenSMIServiceResource = getGreenSMIServiceResource; exports.getGreenSMIServiceResource = getGreenSMIServiceResource;
function validateTrafficSplitsState(kubectl, serviceEntityList) { function validateTrafficSplitsState(kubectl, serviceEntityList) {
var _a;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let trafficSplitsInRightState = true; let trafficSplitsInRightState = true;
for (const serviceObject of serviceEntityList) { for (const serviceObject of serviceEntityList) {
const name = serviceObject.metadata.name; const name = serviceObject.metadata.name;
let trafficSplitObject = yield blueGreenHelper_1.fetchResource(kubectl, exports.TRAFFIC_SPLIT_OBJECT, blueGreenHelper_1.getBlueGreenResourceName(name, exports.TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), (_a = serviceObject === null || serviceObject === void 0 ? void 0 : serviceObject.metadata) === null || _a === void 0 ? void 0 : _a.namespace); let trafficSplitObject = yield blueGreenHelper_1.fetchResource(kubectl, exports.TRAFFIC_SPLIT_OBJECT, blueGreenHelper_1.getBlueGreenResourceName(name, exports.TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX));
core.debug(`ts object extracted was ${JSON.stringify(trafficSplitObject)}`); core.debug(`ts object extracted was ${JSON.stringify(trafficSplitObject)}`);
if (!trafficSplitObject) { if (!trafficSplitObject) {
core.debug(`no traffic split exits for ${name}`); core.debug(`no traffic split exits for ${name}`);
@@ -21380,11 +21356,9 @@ function cleanupSMI(kubectl, serviceEntityList) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const deleteList = []; const deleteList = [];
serviceEntityList.forEach((serviceObject) => { serviceEntityList.forEach((serviceObject) => {
var _a;
deleteList.push({ deleteList.push({
name: blueGreenHelper_1.getBlueGreenResourceName(serviceObject.metadata.name, blueGreenHelper_1.GREEN_SUFFIX), name: blueGreenHelper_1.getBlueGreenResourceName(serviceObject.metadata.name, blueGreenHelper_1.GREEN_SUFFIX),
kind: serviceObject.kind, kind: serviceObject.kind
namespace: (_a = serviceObject === null || serviceObject === void 0 ? void 0 : serviceObject.metadata) === null || _a === void 0 ? void 0 : _a.namespace
}); });
}); });
// delete all objects // delete all objects
@@ -21548,12 +21522,11 @@ function addCanaryLabelsAndAnnotations(inputObject, type) {
} }
} }
function cleanUpCanary(kubectl, files, includeServices) { function cleanUpCanary(kubectl, files, includeServices) {
var _a;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const deleteObject = function (kind, name, namespace) { const deleteObject = function (kind, name) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
const result = yield kubectl.delete([kind, name], namespace); const result = yield kubectl.delete([kind, name]);
kubectlUtils_1.checkForErrors([result]); kubectlUtils_1.checkForErrors([result]);
} }
catch (ex) { catch (ex) {
@@ -21568,14 +21541,13 @@ function cleanUpCanary(kubectl, files, includeServices) {
for (const inputObject of parsedYaml) { for (const inputObject of parsedYaml) {
const name = inputObject.metadata.name; const name = inputObject.metadata.name;
const kind = inputObject.kind; const kind = inputObject.kind;
const namespace = (_a = inputObject === null || inputObject === void 0 ? void 0 : inputObject.metadata) === null || _a === void 0 ? void 0 : _a.namespace;
if (kubernetesTypes_1.isDeploymentEntity(kind) || if (kubernetesTypes_1.isDeploymentEntity(kind) ||
(includeServices && kubernetesTypes_1.isServiceEntity(kind))) { (includeServices && kubernetesTypes_1.isServiceEntity(kind))) {
deletedFiles.push(filePath); deletedFiles.push(filePath);
const canaryObjectName = getCanaryResourceName(name); const canaryObjectName = getCanaryResourceName(name);
const baselineObjectName = getBaselineResourceName(name); const baselineObjectName = getBaselineResourceName(name);
yield deleteObject(kind, canaryObjectName, namespace); yield deleteObject(kind, canaryObjectName);
yield deleteObject(kind, baselineObjectName, namespace); yield deleteObject(kind, baselineObjectName);
} }
} }
} }
@@ -22030,20 +22002,12 @@ function checkManifestStability(kubectl, resources) {
exports.checkManifestStability = checkManifestStability; exports.checkManifestStability = checkManifestStability;
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 defaultWorkflowFileName = 'k8s-deploy-failed-workflow-annotation';
const githubToken = core.getInput('token'); const githubToken = core.getInput('token');
let workflowFilePath; const workflowFilePath = yield githubUtils_1.getWorkflowFilePath(githubToken);
try {
workflowFilePath = yield githubUtils_1.getWorkflowFilePath(githubToken);
}
catch (ex) {
core.warning(`Failed to extract workflow file name: ${ex}`);
workflowFilePath = defaultWorkflowFileName;
}
const deploymentConfig = yield dockerUtils_1.getDeploymentConfig(); const deploymentConfig = yield dockerUtils_1.getDeploymentConfig();
const annotationKeyLabel = workflowAnnotationUtils_1.getWorkflowAnnotationKeyLabel(); const annotationKeyLabel = workflowAnnotationUtils_1.getWorkflowAnnotationKeyLabel();
yield annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, deploymentConfig).catch((err) => core.warning(`Failed to annotate resources: ${err} `)); yield annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, deploymentConfig);
yield labelResources(files, kubectl, annotationKeyLabel).catch((err) => core.warning(`Failed to label resources: ${err}`)); yield labelResources(files, kubectl, annotationKeyLabel);
}); });
} }
exports.annotateAndLabelResources = annotateAndLabelResources; exports.annotateAndLabelResources = annotateAndLabelResources;
@@ -22066,7 +22030,7 @@ function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey
const annotationKeyValStr = `${annotationKey}=${workflowAnnotationUtils_1.getWorkflowAnnotations(lastSuccessSha, workflowFilePath, deploymentConfig)}`; const annotationKeyValStr = `${annotationKey}=${workflowAnnotationUtils_1.getWorkflowAnnotations(lastSuccessSha, workflowFilePath, deploymentConfig)}`;
const annotateNamespace = !(core.getInput('annotate-namespace').toLowerCase() === 'false'); const annotateNamespace = !(core.getInput('annotate-namespace').toLowerCase() === 'false');
if (annotateNamespace) { if (annotateNamespace) {
annotateResults.push(yield kubectl.annotate('namespace', namespace, annotationKeyValStr, namespace)); annotateResults.push(yield kubectl.annotate('namespace', namespace, annotationKeyValStr));
} }
for (const file of files) { for (const file of files) {
try { try {
@@ -22081,7 +22045,7 @@ function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey
if (resource.type.toLowerCase() !== if (resource.type.toLowerCase() !==
models.KubernetesWorkload.POD.toLowerCase()) { models.KubernetesWorkload.POD.toLowerCase()) {
; ;
(yield kubectlUtils_1.annotateChildPods(kubectl, resource.type, resource.name, resource.namespace, annotationKeyValStr, allPods)).forEach((execResult) => annotateResults.push(execResult)); (yield kubectlUtils_1.annotateChildPods(kubectl, resource.type, resource.name, annotationKeyValStr, allPods)).forEach((execResult) => annotateResults.push(execResult));
} }
} }
kubectlUtils_1.checkForErrors(annotateResults, true); kubectlUtils_1.checkForErrors(annotateResults, true);
@@ -22096,7 +22060,7 @@ function labelResources(files, kubectl, label) {
const labelResults = []; const labelResults = [];
for (const file of files) { for (const file of files) {
try { try {
const labelResult = yield kubectl.labelFiles(file, labels); const labelResult = yield kubectl.labelFiles(files, labels);
labelResults.push(labelResult); labelResults.push(labelResult);
} }
catch (e) { catch (e) {
@@ -22344,7 +22308,7 @@ const core = __nccwpck_require__(6024);
const toolCache = __nccwpck_require__(3594); const toolCache = __nccwpck_require__(3594);
const io = __nccwpck_require__(6202); const io = __nccwpck_require__(6202);
class Kubectl { class Kubectl {
constructor(kubectlPath, namespace = '', ignoreSSLErrors = false, resourceGroup = '', name = '') { constructor(kubectlPath, namespace = 'default', ignoreSSLErrors = false, resourceGroup = '', name = '') {
this.kubectlPath = kubectlPath; this.kubectlPath = kubectlPath;
this.ignoreSSLErrors = !!ignoreSSLErrors; this.ignoreSSLErrors = !!ignoreSSLErrors;
this.namespace = namespace; this.namespace = namespace;
@@ -22363,41 +22327,37 @@ class Kubectl {
]; ];
if (force) if (force)
applyArgs.push('--force'); applyArgs.push('--force');
return yield this.execute(applyArgs.concat(this.getFlags())); return yield this.execute(applyArgs);
} }
catch (err) { catch (err) {
core.debug('Kubectl apply failed:' + err); core.debug('Kubectl apply failed:' + err);
} }
}); });
} }
describe(resourceType, resourceName, silent = false, namespace) { describe(resourceType, resourceName, silent = false) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return yield this.execute(['describe', resourceType, resourceName].concat(this.getFlags(namespace)), silent); return yield this.execute(['describe', resourceType, resourceName], silent);
}); });
} }
getNewReplicaSet(deployment, namespace) { getNewReplicaSet(deployment) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const result = yield this.describe('deployment', deployment, true, namespace); const result = yield this.describe('deployment', deployment, true);
let newReplicaSet = ''; let newReplicaSet = '';
if (result === null || result === void 0 ? void 0 : result.stdout) { if (result === null || result === void 0 ? void 0 : result.stdout) {
const stdout = result.stdout.split('\n'); const stdout = result.stdout.split('\n');
core.debug('stdout from getNewReplicaSet is ' + JSON.stringify(stdout));
stdout.forEach((line) => { stdout.forEach((line) => {
const newreplicaset = 'newreplicaset'; const newreplicaset = 'newreplicaset';
if (line && line.toLowerCase().indexOf(newreplicaset) > -1) { if (line && line.toLowerCase().indexOf(newreplicaset) > -1)
core.debug(`found string of interest for replicaset, line is ${line}`);
core.debug(`substring is ${line.substring(newreplicaset.length).trim()}`);
newReplicaSet = line newReplicaSet = line
.substring(newreplicaset.length) .substring(newreplicaset.length)
.trim() .trim()
.split(' ')[0]; .split(' ')[0];
}
}); });
} }
return newReplicaSet; return newReplicaSet;
}); });
} }
annotate(resourceType, resourceName, annotation, namespace) { annotate(resourceType, resourceName, annotation) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const args = [ const args = [
'annotate', 'annotate',
@@ -22405,11 +22365,11 @@ class Kubectl {
resourceName, resourceName,
annotation, annotation,
'--overwrite' '--overwrite'
].concat(this.getFlags(namespace)); ];
return yield this.execute(args); return yield this.execute(args);
}); });
} }
annotateFiles(files, annotation, namespace) { annotateFiles(files, annotation) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const filesToAnnotate = arrayUtils_1.createInlineArray(files); const filesToAnnotate = arrayUtils_1.createInlineArray(files);
core.debug(`annotating ${filesToAnnotate} with annotation ${annotation}`); core.debug(`annotating ${filesToAnnotate} with annotation ${annotation}`);
@@ -22419,11 +22379,12 @@ class Kubectl {
filesToAnnotate, filesToAnnotate,
annotation, annotation,
'--overwrite' '--overwrite'
].concat(this.getFlags(namespace)); ];
core.debug(`sending args from annotate to execute: ${JSON.stringify(args)}`);
return yield this.execute(args); return yield this.execute(args);
}); });
} }
labelFiles(files, labels, namespace) { labelFiles(files, labels) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const args = [ const args = [
'label', 'label',
@@ -22431,53 +22392,56 @@ class Kubectl {
arrayUtils_1.createInlineArray(files), arrayUtils_1.createInlineArray(files),
...labels, ...labels,
'--overwrite' '--overwrite'
].concat(this.getFlags(namespace)); ];
return yield this.execute(args); return yield this.execute(args);
}); });
} }
getAllPods() { getAllPods() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return yield this.execute(['get', 'pods', '-o', 'json'].concat(this.getFlags()), true); return yield this.execute(['get', 'pods', '-o', 'json'], true);
}); });
} }
checkRolloutStatus(resourceType, name, namespace) { checkRolloutStatus(resourceType, name) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return yield this.execute(['rollout', 'status', `${resourceType}/${name}`].concat(this.getFlags(namespace))); return yield this.execute([
'rollout',
'status',
`${resourceType}/${name}`
]);
}); });
} }
getResource(resourceType, name, silentFailure = false, namespace) { getResource(resourceType, name, silentFailure = false) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
core.debug('fetching resource of type ' + resourceType + ' and name ' + name); core.debug('fetching resource of type ' + resourceType + ' and name ' + name);
return yield this.execute(['get', `${resourceType}/${name}`, '-o', 'json'].concat(this.getFlags(namespace)), silentFailure); return yield this.execute(['get', `${resourceType}/${name}`, '-o', 'json'], silentFailure);
}); });
} }
executeCommand(command, args) { executeCommand(command, args) {
if (!command) if (!command)
throw new Error('Command must be defined'); throw new Error('Command must be defined');
const a = args ? [args] : []; return args ? this.execute([command, args]) : this.execute([command]);
return this.execute([command, ...a.concat(this.getFlags())]);
} }
delete(args, namespace) { delete(args) {
if (typeof args === 'string') if (typeof args === 'string')
return this.execute(['delete', args].concat(this.getFlags(namespace))); return this.execute(['delete', args]);
return this.execute(['delete', ...args.concat(this.getFlags(namespace))]); return this.execute(['delete', ...args]);
} }
execute(args, silent = false) { execute(args, silent = false) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
args = args.concat(this.getExecuteFlags());
core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`); core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`);
return yield exec_1.getExecOutput(this.kubectlPath, args, { return yield exec_1.getExecOutput(this.kubectlPath, args, {
silent silent
}); });
}); });
} }
getFlags(namespaceOverride) { getExecuteFlags() {
const flags = []; const flags = [];
if (this.ignoreSSLErrors) { if (this.ignoreSSLErrors) {
flags.push('--insecure-skip-tls-verify'); flags.push('--insecure-skip-tls-verify');
} }
const ns = namespaceOverride || this.namespace; if (this.namespace) {
if (ns) { flags.push('--namespace', this.namespace);
flags.push('--namespace', ns);
} }
return flags; return flags;
} }
@@ -22608,6 +22572,7 @@ const path = __nccwpck_require__(1017);
class PrivateKubectl extends kubectl_1.Kubectl { class PrivateKubectl extends kubectl_1.Kubectl {
execute(args, silent = false) { execute(args, silent = false) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
args = args.concat(this.getExecuteFlags());
args.unshift('kubectl'); args.unshift('kubectl');
let kubectlCmd = args.join(' '); let kubectlCmd = args.join(' ');
let addFileFlag = false; let addFileFlag = false;
@@ -22657,13 +22622,10 @@ class PrivateKubectl extends kubectl_1.Kubectl {
core.debug(`full form of az command: az ${allArgs.join(' ')}`); core.debug(`full form of az command: az ${allArgs.join(' ')}`);
const runOutput = yield exec_1.getExecOutput('az', allArgs, eo); const runOutput = yield exec_1.getExecOutput('az', allArgs, eo);
core.debug(`from kubectl private cluster command got run output ${JSON.stringify(runOutput)}`); core.debug(`from kubectl private cluster command got run output ${JSON.stringify(runOutput)}`);
if (runOutput.exitCode !== 0) {
throw Error(`Call to private cluster failed. Command: '${kubectlCmd}', errormessage: ${runOutput.stderr}`);
}
const runObj = JSON.parse(runOutput.stdout); const runObj = JSON.parse(runOutput.stdout);
if (!silent) if (!silent)
core.info(runObj.logs); core.info(runObj.logs);
if (runObj.exitCode !== 0) { if (runOutput.exitCode !== 0 && runObj.exitCode !== 0) {
throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`); throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`);
} }
return { return {
@@ -23171,7 +23133,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.annotateChildPods = exports.getLastSuccessfulRunSha = exports.checkForErrors = void 0; exports.annotateChildPods = exports.getLastSuccessfulRunSha = exports.checkForErrors = void 0;
const core = __nccwpck_require__(6024); const core = __nccwpck_require__(6024);
const NAMESPACE = 'namespace';
function checkForErrors(execResults, warnIfError) { function checkForErrors(execResults, warnIfError) {
let stderr = ''; let stderr = '';
execResults.forEach((result) => { execResults.forEach((result) => {
@@ -23195,7 +23156,7 @@ exports.checkForErrors = checkForErrors;
function getLastSuccessfulRunSha(kubectl, namespaceName, annotationKey) { function getLastSuccessfulRunSha(kubectl, namespaceName, annotationKey) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
const result = yield kubectl.getResource(NAMESPACE, namespaceName, false, namespaceName); const result = yield kubectl.getResource('namespace', namespaceName);
if (result === null || result === void 0 ? void 0 : result.stderr) { if (result === null || result === void 0 ? void 0 : result.stderr) {
core.warning(result.stderr); core.warning(result.stderr);
return process.env.GITHUB_SHA; return process.env.GITHUB_SHA;
@@ -23218,12 +23179,12 @@ function getLastSuccessfulRunSha(kubectl, namespaceName, annotationKey) {
}); });
} }
exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha; exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha;
function annotateChildPods(kubectl, resourceType, resourceName, namespace, annotationKeyValStr, allPods) { function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyValStr, allPods) {
var _a; var _a;
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let owner = resourceName; let owner = resourceName;
if (resourceType.toLowerCase().indexOf('deployment') > -1) { if (resourceType.toLowerCase().indexOf('deployment') > -1) {
owner = yield kubectl.getNewReplicaSet(resourceName, namespace); owner = yield kubectl.getNewReplicaSet(resourceName);
} }
const commandExecutionResults = []; const commandExecutionResults = [];
if ((allPods === null || allPods === void 0 ? void 0 : allPods.items) && ((_a = allPods.items) === null || _a === void 0 ? void 0 : _a.length) > 0) { if ((allPods === null || allPods === void 0 ? void 0 : allPods.items) && ((_a = allPods.items) === null || _a === void 0 ? void 0 : _a.length) > 0) {
@@ -23233,7 +23194,7 @@ function annotateChildPods(kubectl, resourceType, resourceName, namespace, annot
if (owners) { if (owners) {
for (const ownerRef of owners) { for (const ownerRef of owners) {
if (ownerRef.name === owner) { if (ownerRef.name === owner) {
commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, annotationKeyValStr, namespace)); commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, annotationKeyValStr));
break; break;
} }
} }
@@ -23388,8 +23349,6 @@ const core = __nccwpck_require__(6024);
const KubernetesConstants = __nccwpck_require__(6583); const KubernetesConstants = __nccwpck_require__(6583);
const kubectlUtils_1 = __nccwpck_require__(5474); const kubectlUtils_1 = __nccwpck_require__(5474);
const timeUtils_1 = __nccwpck_require__(4046); const timeUtils_1 = __nccwpck_require__(4046);
const IS_SILENT = false;
const POD = 'pod';
function checkManifestStability(kubectl, resources) { function checkManifestStability(kubectl, resources) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
let rolloutStatusHasErrors = false; let rolloutStatusHasErrors = false;
@@ -23397,32 +23356,32 @@ function checkManifestStability(kubectl, resources) {
const resource = resources[i]; const resource = resources[i];
if (KubernetesConstants.WORKLOAD_TYPES_WITH_ROLLOUT_STATUS.indexOf(resource.type.toLowerCase()) >= 0) { if (KubernetesConstants.WORKLOAD_TYPES_WITH_ROLLOUT_STATUS.indexOf(resource.type.toLowerCase()) >= 0) {
try { try {
const result = yield kubectl.checkRolloutStatus(resource.type, resource.name, resource.namespace); const result = yield kubectl.checkRolloutStatus(resource.type, resource.name);
kubectlUtils_1.checkForErrors([result]); kubectlUtils_1.checkForErrors([result]);
} }
catch (ex) { catch (ex) {
core.error(ex); core.error(ex);
yield kubectl.describe(resource.type, resource.name, IS_SILENT, resource.namespace); yield kubectl.describe(resource.type, resource.name);
rolloutStatusHasErrors = true; rolloutStatusHasErrors = true;
} }
} }
if (resource.type == KubernetesConstants.KubernetesWorkload.POD) { if (resource.type == KubernetesConstants.KubernetesWorkload.POD) {
try { try {
yield checkPodStatus(kubectl, resource); yield checkPodStatus(kubectl, resource.name);
} }
catch (ex) { catch (ex) {
core.warning(`Could not determine pod status: ${JSON.stringify(ex)}`); core.warning(`Could not determine pod status: ${JSON.stringify(ex)}`);
yield kubectl.describe(resource.type, resource.name, IS_SILENT, resource.namespace); yield kubectl.describe(resource.type, resource.name);
} }
} }
if (resource.type == if (resource.type ==
KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE) { KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE) {
try { try {
const service = yield getService(kubectl, resource); const service = yield getService(kubectl, resource.name);
const { spec, status } = service; const { spec, status } = service;
if (spec.type === KubernetesConstants.ServiceTypes.LOAD_BALANCER) { if (spec.type === KubernetesConstants.ServiceTypes.LOAD_BALANCER) {
if (!isLoadBalancerIPAssigned(status)) { if (!isLoadBalancerIPAssigned(status)) {
yield waitForServiceExternalIPAssignment(kubectl, resource); yield waitForServiceExternalIPAssignment(kubectl, resource.name);
} }
else { else {
core.info(`ServiceExternalIP ${resource.name} ${status.loadBalancer.ingress[0].ip}`); core.info(`ServiceExternalIP ${resource.name} ${status.loadBalancer.ingress[0].ip}`);
@@ -23431,7 +23390,7 @@ function checkManifestStability(kubectl, resources) {
} }
catch (ex) { catch (ex) {
core.warning(`Could not determine service status of: ${resource.name} Error: ${ex}`); core.warning(`Could not determine service status of: ${resource.name} Error: ${ex}`);
yield kubectl.describe(resource.type, resource.name, IS_SILENT, resource.namespace); yield kubectl.describe(resource.type, resource.name);
} }
} }
} }
@@ -23441,7 +23400,7 @@ function checkManifestStability(kubectl, resources) {
}); });
} }
exports.checkManifestStability = checkManifestStability; exports.checkManifestStability = checkManifestStability;
function checkPodStatus(kubectl, pod) { function checkPodStatus(kubectl, podName) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const sleepTimeout = 10 * 1000; // 10 seconds const sleepTimeout = 10 * 1000; // 10 seconds
const iterations = 60; // 60 * 10 seconds timeout = 10 minutes max timeout const iterations = 60; // 60 * 10 seconds timeout = 10 minutes max timeout
@@ -23449,20 +23408,20 @@ function checkPodStatus(kubectl, pod) {
let kubectlDescribeNeeded = false; let kubectlDescribeNeeded = false;
for (let i = 0; i < iterations; i++) { for (let i = 0; i < iterations; i++) {
yield timeUtils_1.sleep(sleepTimeout); yield timeUtils_1.sleep(sleepTimeout);
core.debug(`Polling for pod status: ${pod.name}`); core.debug(`Polling for pod status: ${podName}`);
podStatus = yield getPodStatus(kubectl, pod); podStatus = yield getPodStatus(kubectl, podName);
if (podStatus && if (podStatus &&
(podStatus === null || podStatus === void 0 ? void 0 : podStatus.phase) !== 'Pending' && (podStatus === null || podStatus === void 0 ? void 0 : podStatus.phase) !== 'Pending' &&
(podStatus === null || podStatus === void 0 ? void 0 : podStatus.phase) !== 'Unknown') { (podStatus === null || podStatus === void 0 ? void 0 : podStatus.phase) !== 'Unknown') {
break; break;
} }
} }
podStatus = yield getPodStatus(kubectl, pod); podStatus = yield getPodStatus(kubectl, podName);
switch (podStatus.phase) { switch (podStatus.phase) {
case 'Succeeded': case 'Succeeded':
case 'Running': case 'Running':
if (isPodReady(podStatus)) { if (isPodReady(podStatus)) {
console.log(`pod/${pod.name} is successfully rolled out`); console.log(`pod/${podName} is successfully rolled out`);
} }
else { else {
kubectlDescribeNeeded = true; kubectlDescribeNeeded = true;
@@ -23470,26 +23429,26 @@ function checkPodStatus(kubectl, pod) {
break; break;
case 'Pending': case 'Pending':
if (!isPodReady(podStatus)) { if (!isPodReady(podStatus)) {
core.warning(`pod/${pod.name} rollout status check timed out`); core.warning(`pod/${podName} rollout status check timed out`);
kubectlDescribeNeeded = true; kubectlDescribeNeeded = true;
} }
break; break;
case 'Failed': case 'Failed':
core.error(`pod/${pod.name} rollout failed`); core.error(`pod/${podName} rollout failed`);
kubectlDescribeNeeded = true; kubectlDescribeNeeded = true;
break; break;
default: default:
core.warning(`pod/${pod.name} rollout status: ${podStatus.phase}`); core.warning(`pod/${podName} rollout status: ${podStatus.phase}`);
} }
if (kubectlDescribeNeeded) { if (kubectlDescribeNeeded) {
yield kubectl.describe(POD, pod.name, IS_SILENT, pod.namespace); yield kubectl.describe('pod', podName);
} }
}); });
} }
exports.checkPodStatus = checkPodStatus; exports.checkPodStatus = checkPodStatus;
function getPodStatus(kubectl, pod) { function getPodStatus(kubectl, podName) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const podResult = yield kubectl.getResource(POD, pod.name, IS_SILENT, pod.namespace); const podResult = yield kubectl.getResource('pod', podName);
kubectlUtils_1.checkForErrors([podResult]); kubectlUtils_1.checkForErrors([podResult]);
return JSON.parse(podResult.stdout).status; return JSON.parse(podResult.stdout).status;
}); });
@@ -23507,27 +23466,27 @@ function isPodReady(podStatus) {
} }
return allContainersAreReady; return allContainersAreReady;
} }
function getService(kubectl, service) { function getService(kubectl, serviceName) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const serviceResult = yield kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE, service.name, IS_SILENT, service.namespace); const serviceResult = yield kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE, serviceName);
kubectlUtils_1.checkForErrors([serviceResult]); kubectlUtils_1.checkForErrors([serviceResult]);
return JSON.parse(serviceResult.stdout); return JSON.parse(serviceResult.stdout);
}); });
} }
function waitForServiceExternalIPAssignment(kubectl, service) { function waitForServiceExternalIPAssignment(kubectl, serviceName) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const sleepTimeout = 10 * 1000; // 10 seconds const sleepTimeout = 10 * 1000; // 10 seconds
const iterations = 18; // 18 * 10 seconds timeout = 3 minutes max timeout const iterations = 18; // 18 * 10 seconds timeout = 3 minutes max timeout
for (let i = 0; i < iterations; i++) { for (let i = 0; i < iterations; i++) {
core.info(`Wait for service ip assignment : ${service.name}`); core.info(`Wait for service ip assignment : ${serviceName}`);
yield timeUtils_1.sleep(sleepTimeout); yield timeUtils_1.sleep(sleepTimeout);
const status = (yield getService(kubectl, service)).status; const status = (yield getService(kubectl, serviceName)).status;
if (isLoadBalancerIPAssigned(status)) { if (isLoadBalancerIPAssigned(status)) {
core.info(`ServiceExternalIP ${service.name} ${status.loadBalancer.ingress[0].ip}`); core.info(`ServiceExternalIP ${serviceName} ${status.loadBalancer.ingress[0].ip}`);
return; return;
} }
} }
core.warning(`Wait for service ip assignment timed out ${service.name}`); core.warning(`Wait for service ip assignment timed out${serviceName}`);
}); });
} }
function isLoadBalancerIPAssigned(status) { function isLoadBalancerIPAssigned(status) {
@@ -23736,13 +23695,11 @@ function getResources(filePaths, filterResourceTypes) {
filePaths.forEach((filePath) => { filePaths.forEach((filePath) => {
const fileContents = fs.readFileSync(filePath).toString(); const fileContents = fs.readFileSync(filePath).toString();
yaml.safeLoadAll(fileContents, (inputObject) => { yaml.safeLoadAll(fileContents, (inputObject) => {
var _a;
const inputObjectKind = (inputObject === null || inputObject === void 0 ? void 0 : inputObject.kind) || ''; const inputObjectKind = (inputObject === null || inputObject === void 0 ? void 0 : inputObject.kind) || '';
if (filterResourceTypes.filter((type) => inputObjectKind.toLowerCase() === type.toLowerCase()).length > 0) { if (filterResourceTypes.filter((type) => inputObjectKind.toLowerCase() === type.toLowerCase()).length > 0) {
resources.push({ resources.push({
type: inputObject.kind, type: inputObject.kind,
name: inputObject.metadata.name, name: inputObject.metadata.name
namespace: (_a = inputObject === null || inputObject === void 0 ? void 0 : inputObject.metadata) === null || _a === void 0 ? void 0 : _a.namespace
}); });
} }
}); });
@@ -23831,9 +23788,8 @@ exports.getTrafficSplitAPIVersion = getTrafficSplitAPIVersion;
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", ({ value: true })); Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.removeInvalidLabelCharacters = exports.cleanLabel = exports.getWorkflowAnnotationKeyLabel = exports.getWorkflowAnnotations = exports.VALID_LABEL_REGEX = void 0; exports.cleanLabel = exports.getWorkflowAnnotationKeyLabel = exports.getWorkflowAnnotations = void 0;
const ANNOTATION_PREFIX = 'actions.github.com'; const ANNOTATION_PREFIX = 'actions.github.com';
exports.VALID_LABEL_REGEX = /([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]/;
function getWorkflowAnnotations(lastSuccessRunSha, workflowFilePath, deploymentConfig) { function getWorkflowAnnotations(lastSuccessRunSha, workflowFilePath, deploymentConfig) {
const annotationObject = { const annotationObject = {
run: process.env.GITHUB_RUN_ID, run: process.env.GITHUB_RUN_ID,
@@ -23865,20 +23821,14 @@ exports.getWorkflowAnnotationKeyLabel = getWorkflowAnnotationKeyLabel;
* @returns cleaned label * @returns cleaned label
*/ */
function cleanLabel(label) { function cleanLabel(label) {
let removedInvalidChars = removeInvalidLabelCharacters(label); let removedInvalidChars = label
const regexResult = exports.VALID_LABEL_REGEX.exec(removedInvalidChars) || [
'github-workflow-file'
];
return regexResult[0];
}
exports.cleanLabel = cleanLabel;
function removeInvalidLabelCharacters(label) {
return label
.replace(/\s/gi, '_') .replace(/\s/gi, '_')
.replace(/[\/\\\|]/gi, '-') .replace(/[\/\\\|]/gi, '-')
.replace(/[^-A-Za-z0-9_.]/gi, ''); .replace(/[^-A-Za-z0-9_.]/gi, '');
const regex = /([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]/;
return regex.exec(removedInvalidChars)[0] || '';
} }
exports.removeInvalidLabelCharacters = removeInvalidLabelCharacters; exports.cleanLabel = cleanLabel;
/***/ }), /***/ }),
+215 -23
View File
@@ -9,7 +9,7 @@
"version": "0.0.0", "version": "0.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.10.0", "@actions/core": "^1.9.1",
"@actions/exec": "^1.0.0", "@actions/exec": "^1.0.0",
"@actions/io": "^1.0.0", "@actions/io": "^1.0.0",
"@actions/tool-cache": "1.1.2", "@actions/tool-cache": "1.1.2",
@@ -22,17 +22,17 @@
"@types/jest": "^26.0.0", "@types/jest": "^26.0.0",
"@types/js-yaml": "^3.12.7", "@types/js-yaml": "^3.12.7",
"@types/node": "^12.20.41", "@types/node": "^12.20.41",
"@vercel/ncc": "^0.36.1",
"jest": "^26.0.0", "jest": "^26.0.0",
"ncc": "^0.3.6",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"ts-jest": "^26.0.0", "ts-jest": "^26.0.0",
"typescript": "3.9.5" "typescript": "3.9.5"
} }
}, },
"node_modules/@actions/core": { "node_modules/@actions/core": {
"version": "1.10.0", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
"dependencies": { "dependencies": {
"@actions/http-client": "^2.0.1", "@actions/http-client": "^2.0.1",
"uuid": "^8.3.2" "uuid": "^8.3.2"
@@ -1183,15 +1183,6 @@
"integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
"dev": true "dev": true
}, },
"node_modules/@vercel/ncc": {
"version": "0.36.1",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.36.1.tgz",
"integrity": "sha512-S4cL7Taa9yb5qbv+6wLgiKVZ03Qfkc4jGRuiUQMQ8HGBD5pcNRnHeYM33zBvJE4/zJGjJJ8GScB+WmTsn9mORw==",
"dev": true,
"bin": {
"ncc": "dist/ncc/cli.js"
}
},
"node_modules/abab": { "node_modules/abab": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -1884,6 +1875,15 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true "dev": true
}, },
"node_modules/colors": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.2.3.tgz",
"integrity": "sha512-qTfM2pNFeMZcLvf/RbrVAzDEVttZjFhaApfx9dplNjvHSX88Ui66zBRb/4YGob/xUWxDceirgoC1lT676asfCQ==",
"dev": true,
"engines": {
"node": ">=0.1.90"
}
},
"node_modules/combined-stream": { "node_modules/combined-stream": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -1978,6 +1978,15 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/dateformat": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
"integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/debug": { "node_modules/debug": {
"version": "4.3.4", "version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -4082,6 +4091,58 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true "dev": true
}, },
"node_modules/ncc": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/ncc/-/ncc-0.3.6.tgz",
"integrity": "sha512-OXudTB2Ebt/FnOuDoPQbaa17+tdVqSOWA+gLfPxccWwsNED1uA2zEhpoB1hwdFC9yYbio/mdV5cvOtQI3Zrx1w==",
"dev": true,
"dependencies": {
"mkdirp": "^0.5.1",
"rimraf": "^2.6.1",
"tracer": "^0.8.7",
"ws": "^2.3.1"
}
},
"node_modules/ncc/node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/ncc/node_modules/rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
}
},
"node_modules/ncc/node_modules/safe-buffer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
"integrity": "sha512-cr7dZWLwOeaFBLTIuZeYdkfO7UzGIKhjYENJFAxUOMKWGaWDm2nJM2rzxNRm5Owu0DH3ApwNo6kx5idXZfb/Iw==",
"dev": true
},
"node_modules/ncc/node_modules/ws": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz",
"integrity": "sha512-61a+9LgtYZxTq1hAonhX8Xwpo2riK4IOR/BIVxioFbCfc3QFKmpE4x9dLExfLHKtUfVZigYa36tThVhO57erEw==",
"dev": true,
"dependencies": {
"safe-buffer": "~5.0.1",
"ultron": "~1.1.0"
}
},
"node_modules/nice-try": { "node_modules/nice-try": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@@ -5822,6 +5883,15 @@
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
"dev": true "dev": true
}, },
"node_modules/tinytim": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/tinytim/-/tinytim-0.1.1.tgz",
"integrity": "sha512-NIpsp9lBIxPNzB++HnMmUd4byzJSVbbO4F+As1Gb1IG/YQT5QvmBDjpx8SpDS8fhGC+t+Qw8ldQgbcAIaU+2cA==",
"dev": true,
"engines": {
"node": ">= 0.2.0"
}
},
"node_modules/tmpl": { "node_modules/tmpl": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -5914,6 +5984,33 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/tracer": {
"version": "0.8.15",
"resolved": "https://registry.npmjs.org/tracer/-/tracer-0.8.15.tgz",
"integrity": "sha512-ZQzlhd6zZFIpAhACiZkxLjl65XqVwi8t8UEBVGRIHAQN6nj55ftJWiFell+WSqWCP/vEycrIbUSuiyMwul+TFw==",
"dev": true,
"dependencies": {
"colors": "1.2.3",
"dateformat": "3.0.3",
"mkdirp": "^0.5.1",
"tinytim": "0.1.1"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/tracer/node_modules/mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"dependencies": {
"minimist": "^1.2.6"
},
"bin": {
"mkdirp": "bin/cmd.js"
}
},
"node_modules/ts-jest": { "node_modules/ts-jest": {
"version": "26.5.6", "version": "26.5.6",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz",
@@ -6030,6 +6127,12 @@
"node": ">=4.2.0" "node": ">=4.2.0"
} }
}, },
"node_modules/ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
"dev": true
},
"node_modules/underscore": { "node_modules/underscore": {
"version": "1.13.4", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
@@ -6426,9 +6529,9 @@
}, },
"dependencies": { "dependencies": {
"@actions/core": { "@actions/core": {
"version": "1.10.0", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==", "integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
"requires": { "requires": {
"@actions/http-client": "^2.0.1", "@actions/http-client": "^2.0.1",
"uuid": "^8.3.2" "uuid": "^8.3.2"
@@ -7390,12 +7493,6 @@
"integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
"dev": true "dev": true
}, },
"@vercel/ncc": {
"version": "0.36.1",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.36.1.tgz",
"integrity": "sha512-S4cL7Taa9yb5qbv+6wLgiKVZ03Qfkc4jGRuiUQMQ8HGBD5pcNRnHeYM33zBvJE4/zJGjJJ8GScB+WmTsn9mORw==",
"dev": true
},
"abab": { "abab": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -7923,6 +8020,12 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true "dev": true
}, },
"colors": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.2.3.tgz",
"integrity": "sha512-qTfM2pNFeMZcLvf/RbrVAzDEVttZjFhaApfx9dplNjvHSX88Ui66zBRb/4YGob/xUWxDceirgoC1lT676asfCQ==",
"dev": true
},
"combined-stream": { "combined-stream": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -8004,6 +8107,12 @@
"whatwg-url": "^8.0.0" "whatwg-url": "^8.0.0"
} }
}, },
"dateformat": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
"integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==",
"dev": true
},
"debug": { "debug": {
"version": "4.3.4", "version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -9619,6 +9728,54 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true "dev": true
}, },
"ncc": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/ncc/-/ncc-0.3.6.tgz",
"integrity": "sha512-OXudTB2Ebt/FnOuDoPQbaa17+tdVqSOWA+gLfPxccWwsNED1uA2zEhpoB1hwdFC9yYbio/mdV5cvOtQI3Zrx1w==",
"dev": true,
"requires": {
"mkdirp": "^0.5.1",
"rimraf": "^2.6.1",
"tracer": "^0.8.7",
"ws": "^2.3.1"
},
"dependencies": {
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"requires": {
"minimist": "^1.2.6"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"dev": true,
"requires": {
"glob": "^7.1.3"
}
},
"safe-buffer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
"integrity": "sha512-cr7dZWLwOeaFBLTIuZeYdkfO7UzGIKhjYENJFAxUOMKWGaWDm2nJM2rzxNRm5Owu0DH3ApwNo6kx5idXZfb/Iw==",
"dev": true
},
"ws": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz",
"integrity": "sha512-61a+9LgtYZxTq1hAonhX8Xwpo2riK4IOR/BIVxioFbCfc3QFKmpE4x9dLExfLHKtUfVZigYa36tThVhO57erEw==",
"dev": true,
"requires": {
"safe-buffer": "~5.0.1",
"ultron": "~1.1.0"
}
}
}
},
"nice-try": { "nice-try": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
@@ -10982,6 +11139,12 @@
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
"dev": true "dev": true
}, },
"tinytim": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/tinytim/-/tinytim-0.1.1.tgz",
"integrity": "sha512-NIpsp9lBIxPNzB++HnMmUd4byzJSVbbO4F+As1Gb1IG/YQT5QvmBDjpx8SpDS8fhGC+t+Qw8ldQgbcAIaU+2cA==",
"dev": true
},
"tmpl": { "tmpl": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@@ -11055,6 +11218,29 @@
"punycode": "^2.1.1" "punycode": "^2.1.1"
} }
}, },
"tracer": {
"version": "0.8.15",
"resolved": "https://registry.npmjs.org/tracer/-/tracer-0.8.15.tgz",
"integrity": "sha512-ZQzlhd6zZFIpAhACiZkxLjl65XqVwi8t8UEBVGRIHAQN6nj55ftJWiFell+WSqWCP/vEycrIbUSuiyMwul+TFw==",
"dev": true,
"requires": {
"colors": "1.2.3",
"dateformat": "3.0.3",
"mkdirp": "^0.5.1",
"tinytim": "0.1.1"
},
"dependencies": {
"mkdirp": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true,
"requires": {
"minimist": "^1.2.6"
}
}
}
},
"ts-jest": { "ts-jest": {
"version": "26.5.6", "version": "26.5.6",
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz",
@@ -11135,6 +11321,12 @@
"integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==", "integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==",
"dev": true "dev": true
}, },
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
"dev": true
},
"underscore": { "underscore": {
"version": "1.13.4", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
+2 -2
View File
@@ -11,7 +11,7 @@
"format-check": "prettier --check ." "format-check": "prettier --check ."
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.10.0", "@actions/core": "^1.9.1",
"@actions/exec": "^1.0.0", "@actions/exec": "^1.0.0",
"@actions/io": "^1.0.0", "@actions/io": "^1.0.0",
"@actions/tool-cache": "1.1.2", "@actions/tool-cache": "1.1.2",
@@ -24,8 +24,8 @@
"@types/jest": "^26.0.0", "@types/jest": "^26.0.0",
"@types/js-yaml": "^3.12.7", "@types/js-yaml": "^3.12.7",
"@types/node": "^12.20.41", "@types/node": "^12.20.41",
"@vercel/ncc": "^0.36.1",
"jest": "^26.0.0", "jest": "^26.0.0",
"ncc": "^0.3.6",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"ts-jest": "^26.0.0", "ts-jest": "^26.0.0",
"typescript": "3.9.5" "typescript": "3.9.5"
+1 -3
View File
@@ -56,9 +56,7 @@ export async function deploy(
for (const ingressResource of ingressResources) { for (const ingressResource of ingressResources) {
await kubectl.getResource( await kubectl.getResource(
KubernetesConstants.DiscoveryAndLoadBalancerResource.INGRESS, KubernetesConstants.DiscoveryAndLoadBalancerResource.INGRESS,
ingressResource.name, ingressResource.name
false,
ingressResource.namespace
) )
} }
core.endGroup() core.endGroup()
@@ -38,8 +38,7 @@ export async function deleteGreenObjects(
const resourcesToDelete: K8sDeleteObject[] = toDelete.map((obj) => { const resourcesToDelete: K8sDeleteObject[] = toDelete.map((obj) => {
return { return {
name: getBlueGreenResourceName(obj.metadata.name, GREEN_SUFFIX), name: getBlueGreenResourceName(obj.metadata.name, GREEN_SUFFIX),
kind: obj.kind, kind: obj.kind
namespace: obj.metadata.namespace
} }
}) })
@@ -67,25 +66,31 @@ export async function deleteObjects(
// other common functions // other common functions
export function getManifestObjects(filePaths: string[]): BlueGreenManifests { export function getManifestObjects(filePaths: string[]): BlueGreenManifests {
const deploymentEntityList: K8sObject[] = [] const deploymentEntityList: K8sObject[] = []
const serviceEntityList: K8sObject[] = []
const routedServiceEntityList: K8sObject[] = [] const routedServiceEntityList: K8sObject[] = []
const unroutedServiceEntityList: K8sObject[] = [] const unroutedServiceEntityList: K8sObject[] = []
const ingressEntityList: K8sObject[] = [] const ingressEntityList: K8sObject[] = []
const otherEntitiesList: K8sObject[] = [] const otherEntitiesList: K8sObject[] = []
const serviceNameMap = new Map<string, string>() const serviceNameMap = new Map<string, string>()
// Manifest objects per type. All resources should be parsed and
// organized before we can check if services are “routed” or not.
filePaths.forEach((filePath: string) => { filePaths.forEach((filePath: string) => {
const fileContents = fs.readFileSync(filePath).toString() const fileContents = fs.readFileSync(filePath).toString()
yaml.safeLoadAll(fileContents, (inputObject) => { yaml.safeLoadAll(fileContents, (inputObject) => {
if (!!inputObject) { if (!!inputObject) {
const kind = inputObject.kind const kind = inputObject.kind
const name = inputObject.metadata.name
if (isDeploymentEntity(kind)) { if (isDeploymentEntity(kind)) {
deploymentEntityList.push(inputObject) deploymentEntityList.push(inputObject)
} else if (isServiceEntity(kind)) { } else if (isServiceEntity(kind)) {
serviceEntityList.push(inputObject) if (isServiceRouted(inputObject, deploymentEntityList)) {
routedServiceEntityList.push(inputObject)
serviceNameMap.set(
name,
getBlueGreenResourceName(name, GREEN_SUFFIX)
)
} else {
unroutedServiceEntityList.push(inputObject)
}
} else if (isIngressEntity(kind)) { } else if (isIngressEntity(kind)) {
ingressEntityList.push(inputObject) ingressEntityList.push(inputObject)
} else { } else {
@@ -95,16 +100,6 @@ export function getManifestObjects(filePaths: string[]): BlueGreenManifests {
}) })
}) })
serviceEntityList.forEach((inputObject: any) => {
if (isServiceRouted(inputObject, deploymentEntityList)) {
const name = inputObject.metadata.name
routedServiceEntityList.push(inputObject)
serviceNameMap.set(name, getBlueGreenResourceName(name, GREEN_SUFFIX))
} else {
unroutedServiceEntityList.push(inputObject)
}
})
return { return {
serviceEntityList: routedServiceEntityList, serviceEntityList: routedServiceEntityList,
serviceNameMap: serviceNameMap, serviceNameMap: serviceNameMap,
@@ -239,10 +234,9 @@ export function isServiceSelectorSubsetOfMatchLabel(
export async function fetchResource( export async function fetchResource(
kubectl: Kubectl, kubectl: Kubectl,
kind: string, kind: string,
name: string, name: string
namespace?: string
): Promise<K8sObject> { ): Promise<K8sObject> {
const result = await kubectl.getResource(kind, name, false, namespace) const result = await kubectl.getResource(kind, name)
if (result == null || !!result.stderr) { if (result == null || !!result.stderr) {
return null return null
} }
@@ -97,8 +97,7 @@ export async function validateIngresses(
const existingIngress = await fetchResource( const existingIngress = await fetchResource(
kubectl, kubectl,
inputObject.kind, inputObject.kind,
inputObject.metadata.name, inputObject.metadata.name
inputObject?.metadata?.namespace
) )
const isValid = const isValid =
@@ -31,8 +31,7 @@ export async function validateServicesState(
const existingService = await fetchResource( const existingService = await fetchResource(
kubectl, kubectl,
serviceObject.kind, serviceObject.kind,
serviceObject.metadata.name, serviceObject.metadata.name
serviceObject?.metadata?.namespace
) )
let isServiceGreen = let isServiceGreen =
@@ -142,8 +142,7 @@ export async function validateTrafficSplitsState(
let trafficSplitObject = await fetchResource( let trafficSplitObject = await fetchResource(
kubectl, kubectl,
TRAFFIC_SPLIT_OBJECT, TRAFFIC_SPLIT_OBJECT,
getBlueGreenResourceName(name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), getBlueGreenResourceName(name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX)
serviceObject?.metadata?.namespace
) )
core.debug( core.debug(
`ts object extracted was ${JSON.stringify(trafficSplitObject)}` `ts object extracted was ${JSON.stringify(trafficSplitObject)}`
@@ -184,8 +183,7 @@ export async function cleanupSMI(
serviceObject.metadata.name, serviceObject.metadata.name,
GREEN_SUFFIX GREEN_SUFFIX
), ),
kind: serviceObject.kind, kind: serviceObject.kind
namespace: serviceObject?.metadata?.namespace
}) })
}) })
+4 -9
View File
@@ -195,13 +195,9 @@ async function cleanUpCanary(
files: string[], files: string[],
includeServices: boolean includeServices: boolean
): Promise<string[]> { ): Promise<string[]> {
const deleteObject = async function ( const deleteObject = async function (kind, name) {
kind: string,
name: string,
namespace: string | undefined
) {
try { try {
const result = await kubectl.delete([kind, name], namespace) const result = await kubectl.delete([kind, name])
checkForErrors([result]) checkForErrors([result])
} catch (ex) { } catch (ex) {
// Ignore failures of delete if it doesn't exist // Ignore failures of delete if it doesn't exist
@@ -217,7 +213,6 @@ async function cleanUpCanary(
for (const inputObject of parsedYaml) { for (const inputObject of parsedYaml) {
const name = inputObject.metadata.name const name = inputObject.metadata.name
const kind = inputObject.kind const kind = inputObject.kind
const namespace: string | undefined = inputObject?.metadata?.namespace
if ( if (
isDeploymentEntity(kind) || isDeploymentEntity(kind) ||
@@ -227,8 +222,8 @@ async function cleanUpCanary(
const canaryObjectName = getCanaryResourceName(name) const canaryObjectName = getCanaryResourceName(name)
const baselineObjectName = getBaselineResourceName(name) const baselineObjectName = getBaselineResourceName(name)
await deleteObject(kind, canaryObjectName, namespace) await deleteObject(kind, canaryObjectName)
await deleteObject(kind, baselineObjectName, namespace) await deleteObject(kind, baselineObjectName)
} }
} }
} }
+4 -20
View File
@@ -150,15 +150,8 @@ export async function annotateAndLabelResources(
resourceTypes: Resource[], resourceTypes: Resource[],
allPods: any allPods: any
) { ) {
const defaultWorkflowFileName = 'k8s-deploy-failed-workflow-annotation'
const githubToken = core.getInput('token') const githubToken = core.getInput('token')
let workflowFilePath const workflowFilePath = await getWorkflowFilePath(githubToken)
try {
workflowFilePath = await getWorkflowFilePath(githubToken)
} catch (ex) {
core.warning(`Failed to extract workflow file name: ${ex}`)
workflowFilePath = defaultWorkflowFileName
}
const deploymentConfig = await getDeploymentConfig() const deploymentConfig = await getDeploymentConfig()
const annotationKeyLabel = getWorkflowAnnotationKeyLabel() const annotationKeyLabel = getWorkflowAnnotationKeyLabel()
@@ -171,11 +164,8 @@ export async function annotateAndLabelResources(
annotationKeyLabel, annotationKeyLabel,
workflowFilePath, workflowFilePath,
deploymentConfig deploymentConfig
).catch((err) => core.warning(`Failed to annotate resources: ${err} `))
await labelResources(files, kubectl, annotationKeyLabel).catch((err) =>
core.warning(`Failed to label resources: ${err}`)
) )
await labelResources(files, kubectl, annotationKeyLabel)
} }
async function annotateResources( async function annotateResources(
@@ -218,12 +208,7 @@ async function annotateResources(
) )
if (annotateNamespace) { if (annotateNamespace) {
annotateResults.push( annotateResults.push(
await kubectl.annotate( await kubectl.annotate('namespace', namespace, annotationKeyValStr)
'namespace',
namespace,
annotationKeyValStr,
namespace
)
) )
} }
for (const file of files) { for (const file of files) {
@@ -248,7 +233,6 @@ async function annotateResources(
kubectl, kubectl,
resource.type, resource.type,
resource.name, resource.name,
resource.namespace,
annotationKeyValStr, annotationKeyValStr,
allPods allPods
) )
@@ -274,7 +258,7 @@ async function labelResources(
const labelResults = [] const labelResults = []
for (const file of files) { for (const file of files) {
try { try {
const labelResult = await kubectl.labelFiles(file, labels) const labelResult = await kubectl.labelFiles(files, labels)
labelResults.push(labelResult) labelResults.push(labelResult)
} catch (e) { } catch (e) {
core.warning(`failed to annotate resource: ${e}`) core.warning(`failed to annotate resource: ${e}`)
-2
View File
@@ -2,7 +2,6 @@ export interface K8sObject {
metadata: { metadata: {
name: string name: string
labels: Map<string, string> labels: Map<string, string>
namespace?: string
} }
kind: string kind: string
spec: any spec: any
@@ -17,7 +16,6 @@ export interface K8sServiceObject extends K8sObject {
export interface K8sDeleteObject { export interface K8sDeleteObject {
name: string name: string
kind: string kind: string
namespace?: string
} }
export interface K8sIngress extends K8sObject { export interface K8sIngress extends K8sObject {
+1 -176
View File
@@ -3,6 +3,7 @@ import * as exec from '@actions/exec'
import * as io from '@actions/io' import * as io from '@actions/io'
import * as core from '@actions/core' import * as core from '@actions/core'
import * as toolCache from '@actions/tool-cache' import * as toolCache from '@actions/tool-cache'
import {config} from 'process'
describe('Kubectl path', () => { describe('Kubectl path', () => {
const version = '1.1' const version = '1.1'
@@ -37,7 +38,6 @@ describe('Kubectl path', () => {
const kubectlPath = 'kubectlPath' const kubectlPath = 'kubectlPath'
const testNamespace = 'testNamespace' const testNamespace = 'testNamespace'
const defaultNamespace = 'default' const defaultNamespace = 'default'
const otherNamespace = 'otherns'
describe('Kubectl class', () => { describe('Kubectl class', () => {
describe('default namespace behavior', () => { describe('default namespace behavior', () => {
const kubectl = new Kubectl(kubectlPath, defaultNamespace) const kubectl = new Kubectl(kubectlPath, defaultNamespace)
@@ -122,26 +122,6 @@ describe('Kubectl class', () => {
], ],
{silent: false} {silent: false}
) )
// overrided ns
const silent = false
await kubectl.describe(
resourceType,
resourceName,
silent,
otherNamespace
)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
[
'describe',
resourceType,
resourceName,
'--namespace',
otherNamespace
],
{silent}
)
}) })
it('describes a resource silently', async () => { it('describes a resource silently', async () => {
@@ -160,26 +140,6 @@ describe('Kubectl class', () => {
], ],
{silent: true} {silent: true}
) )
// overrided ns
const silent = false
await kubectl.describe(
resourceType,
resourceName,
silent,
otherNamespace
)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
[
'describe',
resourceType,
resourceName,
'--namespace',
otherNamespace
],
{silent}
)
}) })
it('annotates resource', async () => { it('annotates resource', async () => {
@@ -205,27 +165,6 @@ describe('Kubectl class', () => {
], ],
{silent: false} {silent: false}
) )
// override ns
await kubectl.annotate(
resourceType,
resourceName,
annotation,
otherNamespace
)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
[
'annotate',
resourceType,
resourceName,
annotation,
'--overwrite',
'--namespace',
otherNamespace
],
{silent: false}
)
}) })
it('annotates files with single file', async () => { it('annotates files with single file', async () => {
@@ -246,22 +185,6 @@ describe('Kubectl class', () => {
], ],
{silent: false} {silent: false}
) )
// override ns
await kubectl.annotateFiles(file, annotation, otherNamespace)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
[
'annotate',
'-f',
file,
annotation,
'--overwrite',
'--namespace',
otherNamespace
],
{silent: false}
)
}) })
it('annotates files with mulitple files', async () => { it('annotates files with mulitple files', async () => {
@@ -282,22 +205,6 @@ describe('Kubectl class', () => {
], ],
{silent: false} {silent: false}
) )
// override ns
await kubectl.annotateFiles(files, annotation, otherNamespace)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
[
'annotate',
'-f',
files.join(','),
annotation,
'--overwrite',
'--namespace',
otherNamespace
],
{silent: false}
)
}) })
it('labels files with single file', async () => { it('labels files with single file', async () => {
@@ -318,21 +225,6 @@ describe('Kubectl class', () => {
], ],
{silent: false} {silent: false}
) )
await kubectl.labelFiles(file, labels, otherNamespace)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
[
'label',
'-f',
file,
...labels,
'--overwrite',
'--namespace',
otherNamespace
],
{silent: false}
)
}) })
it('labels files with multiple files', async () => { it('labels files with multiple files', async () => {
@@ -353,21 +245,6 @@ describe('Kubectl class', () => {
], ],
{silent: false} {silent: false}
) )
await kubectl.labelFiles(files, labels, otherNamespace)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
[
'label',
'-f',
files.join(','),
...labels,
'--overwrite',
'--namespace',
otherNamespace
],
{silent: false}
)
}) })
it('gets all pods', async () => { it('gets all pods', async () => {
@@ -396,20 +273,6 @@ describe('Kubectl class', () => {
], ],
{silent: false} {silent: false}
) )
// override ns
await kubectl.checkRolloutStatus(resourceType, name, otherNamespace)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
[
'rollout',
'status',
`${resourceType}/${name}`,
'--namespace',
otherNamespace
],
{silent: false}
)
}) })
it('gets resource', async () => { it('gets resource', async () => {
@@ -428,22 +291,6 @@ describe('Kubectl class', () => {
], ],
{silent: false} {silent: false}
) )
// override ns
const silent = true
await kubectl.getResource(resourceType, name, silent, otherNamespace)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
[
'get',
`${resourceType}/${name}`,
'-o',
'json',
'--namespace',
otherNamespace
],
{silent}
)
}) })
it('executes a command', async () => { it('executes a command', async () => {
@@ -474,14 +321,6 @@ describe('Kubectl class', () => {
['delete', arg, '--namespace', testNamespace], ['delete', arg, '--namespace', testNamespace],
{silent: false} {silent: false}
) )
// override ns
await kubectl.delete(arg, otherNamespace)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
['delete', arg, '--namespace', otherNamespace],
{silent: false}
)
}) })
it('deletes with multiple arguments', async () => { it('deletes with multiple arguments', async () => {
@@ -492,14 +331,6 @@ describe('Kubectl class', () => {
['delete', ...args, '--namespace', testNamespace], ['delete', ...args, '--namespace', testNamespace],
{silent: false} {silent: false}
) )
// override ns
await kubectl.delete(args, otherNamespace)
expect(exec.getExecOutput).toBeCalledWith(
kubectlPath,
['delete', ...args, '--namespace', otherNamespace],
{silent: false}
)
}) })
}) })
@@ -538,11 +369,5 @@ describe('Kubectl class', () => {
[command, '--insecure-skip-tls-verify', '--namespace', testNamespace], [command, '--insecure-skip-tls-verify', '--namespace', testNamespace],
{silent: false} {silent: false}
) )
const kubectlNoFlags = new Kubectl(kubectlPath)
kubectlNoFlags.executeCommand(command)
expect(exec.getExecOutput).toBeCalledWith(kubectlPath, [command], {
silent: false
})
}) })
}) })
+34 -60
View File
@@ -3,11 +3,11 @@ import {createInlineArray} from '../utilities/arrayUtils'
import * as core from '@actions/core' import * as core from '@actions/core'
import * as toolCache from '@actions/tool-cache' import * as toolCache from '@actions/tool-cache'
import * as io from '@actions/io' import * as io from '@actions/io'
import {exec} from 'child_process'
export interface Resource { export interface Resource {
name: string name: string
type: string type: string
namespace?: string
} }
export class Kubectl { export class Kubectl {
@@ -20,7 +20,7 @@ export class Kubectl {
constructor( constructor(
kubectlPath: string, kubectlPath: string,
namespace: string = '', namespace: string = 'default',
ignoreSSLErrors: boolean = false, ignoreSSLErrors: boolean = false,
resourceGroup: string = '', resourceGroup: string = '',
name: string = '' name: string = ''
@@ -47,7 +47,7 @@ export class Kubectl {
] ]
if (force) applyArgs.push('--force') if (force) applyArgs.push('--force')
return await this.execute(applyArgs.concat(this.getFlags())) return await this.execute(applyArgs)
} catch (err) { } catch (err) {
core.debug('Kubectl apply failed:' + err) core.debug('Kubectl apply failed:' + err)
} }
@@ -56,43 +56,27 @@ export class Kubectl {
public async describe( public async describe(
resourceType: string, resourceType: string,
resourceName: string, resourceName: string,
silent: boolean = false, silent: boolean = false
namespace?: string
): Promise<ExecOutput> { ): Promise<ExecOutput> {
return await this.execute( return await this.execute(
['describe', resourceType, resourceName].concat( ['describe', resourceType, resourceName],
this.getFlags(namespace)
),
silent silent
) )
} }
public async getNewReplicaSet(deployment: string, namespace?: string) { public async getNewReplicaSet(deployment: string) {
const result = await this.describe( const result = await this.describe('deployment', deployment, true)
'deployment',
deployment,
true,
namespace
)
let newReplicaSet = '' let newReplicaSet = ''
if (result?.stdout) { if (result?.stdout) {
const stdout = result.stdout.split('\n') const stdout = result.stdout.split('\n')
core.debug('stdout from getNewReplicaSet is ' + JSON.stringify(stdout))
stdout.forEach((line: string) => { stdout.forEach((line: string) => {
const newreplicaset = 'newreplicaset' const newreplicaset = 'newreplicaset'
if (line && line.toLowerCase().indexOf(newreplicaset) > -1) { if (line && line.toLowerCase().indexOf(newreplicaset) > -1)
core.debug(
`found string of interest for replicaset, line is ${line}`
)
core.debug(
`substring is ${line.substring(newreplicaset.length).trim()}`
)
newReplicaSet = line newReplicaSet = line
.substring(newreplicaset.length) .substring(newreplicaset.length)
.trim() .trim()
.split(' ')[0] .split(' ')[0]
}
}) })
} }
@@ -102,8 +86,7 @@ export class Kubectl {
public async annotate( public async annotate(
resourceType: string, resourceType: string,
resourceName: string, resourceName: string,
annotation: string, annotation: string
namespace?: string
): Promise<ExecOutput> { ): Promise<ExecOutput> {
const args = [ const args = [
'annotate', 'annotate',
@@ -111,14 +94,13 @@ export class Kubectl {
resourceName, resourceName,
annotation, annotation,
'--overwrite' '--overwrite'
].concat(this.getFlags(namespace)) ]
return await this.execute(args) return await this.execute(args)
} }
public async annotateFiles( public async annotateFiles(
files: string | string[], files: string | string[],
annotation: string, annotation: string
namespace?: string
): Promise<ExecOutput> { ): Promise<ExecOutput> {
const filesToAnnotate = createInlineArray(files) const filesToAnnotate = createInlineArray(files)
core.debug(`annotating ${filesToAnnotate} with annotation ${annotation}`) core.debug(`annotating ${filesToAnnotate} with annotation ${annotation}`)
@@ -128,14 +110,16 @@ export class Kubectl {
filesToAnnotate, filesToAnnotate,
annotation, annotation,
'--overwrite' '--overwrite'
].concat(this.getFlags(namespace)) ]
core.debug(
`sending args from annotate to execute: ${JSON.stringify(args)}`
)
return await this.execute(args) return await this.execute(args)
} }
public async labelFiles( public async labelFiles(
files: string | string[], files: string | string[],
labels: string[], labels: string[]
namespace?: string
): Promise<ExecOutput> { ): Promise<ExecOutput> {
const args = [ const args = [
'label', 'label',
@@ -143,59 +127,51 @@ export class Kubectl {
createInlineArray(files), createInlineArray(files),
...labels, ...labels,
'--overwrite' '--overwrite'
].concat(this.getFlags(namespace)) ]
return await this.execute(args) return await this.execute(args)
} }
public async getAllPods(): Promise<ExecOutput> { public async getAllPods(): Promise<ExecOutput> {
return await this.execute( return await this.execute(['get', 'pods', '-o', 'json'], true)
['get', 'pods', '-o', 'json'].concat(this.getFlags()),
true
)
} }
public async checkRolloutStatus( public async checkRolloutStatus(
resourceType: string, resourceType: string,
name: string, name: string
namespace?: string
): Promise<ExecOutput> { ): Promise<ExecOutput> {
return await this.execute( return await this.execute([
['rollout', 'status', `${resourceType}/${name}`].concat( 'rollout',
this.getFlags(namespace) 'status',
) `${resourceType}/${name}`
) ])
} }
public async getResource( public async getResource(
resourceType: string, resourceType: string,
name: string, name: string,
silentFailure: boolean = false, silentFailure: boolean = false
namespace?: string
): Promise<ExecOutput> { ): Promise<ExecOutput> {
core.debug( core.debug(
'fetching resource of type ' + resourceType + ' and name ' + name 'fetching resource of type ' + resourceType + ' and name ' + name
) )
return await this.execute( return await this.execute(
['get', `${resourceType}/${name}`, '-o', 'json'].concat( ['get', `${resourceType}/${name}`, '-o', 'json'],
this.getFlags(namespace)
),
silentFailure silentFailure
) )
} }
public executeCommand(command: string, args?: string) { public executeCommand(command: string, args?: string) {
if (!command) throw new Error('Command must be defined') if (!command) throw new Error('Command must be defined')
const a = args ? [args] : [] return args ? this.execute([command, args]) : this.execute([command])
return this.execute([command, ...a.concat(this.getFlags())])
} }
public delete(args: string | string[], namespace?: string) { public delete(args: string | string[]) {
if (typeof args === 'string') if (typeof args === 'string') return this.execute(['delete', args])
return this.execute(['delete', args].concat(this.getFlags(namespace))) return this.execute(['delete', ...args])
return this.execute(['delete', ...args.concat(this.getFlags(namespace))])
} }
protected async execute(args: string[], silent: boolean = false) { protected async execute(args: string[], silent: boolean = false) {
args = args.concat(this.getExecuteFlags())
core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`) core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`)
return await getExecOutput(this.kubectlPath, args, { return await getExecOutput(this.kubectlPath, args, {
@@ -203,15 +179,13 @@ export class Kubectl {
}) })
} }
protected getFlags(namespaceOverride?: string): string[] { protected getExecuteFlags(): string[] {
const flags = [] const flags = []
if (this.ignoreSSLErrors) { if (this.ignoreSSLErrors) {
flags.push('--insecure-skip-tls-verify') flags.push('--insecure-skip-tls-verify')
} }
if (this.namespace) {
const ns = namespaceOverride || this.namespace flags.push('--namespace', this.namespace)
if (ns) {
flags.push('--namespace', ns)
} }
return flags return flags
+1 -21
View File
@@ -1,32 +1,12 @@
import {PrivateKubectl} from './privatekubectl' import {PrivateKubectl} from './privatekubectl'
import * as exec from '@actions/exec'
describe('Private kubectl', () => { describe('Private kubectl', () => {
const testString = `kubectl annotate -f test.yml,test2.yml,test3.yml -f test4.yml --filename test5.yml actions.github.com/k8s-deploy={"run":"3498366832","repository":"jaiveerk/k8s-deploy","workflow":"Minikube Integration Tests - private cluster","workflowFileName":"run-integration-tests-private.yml","jobName":"run-integration-test","createdBy":"jaiveerk","runUri":"https://github.com/jaiveerk/k8s-deploy/actions/runs/3498366832","commit":"c63b323186ea1320a31290de6dcc094c06385e75","lastSuccessRunCommit":"NA","branch":"refs/heads/main","deployTimestamp":1668787848577,"dockerfilePaths":{"nginx:1.14.2":""},"manifestsPaths":["https://github.com/jaiveerk/k8s-deploy/blob/c63b323186ea1320a31290de6dcc094c06385e75/test/integration/manifests/test.yml"],"helmChartPaths":[],"provider":"GitHub"} --overwrite --namespace test-3498366832` const testString = `kubectl annotate -f test.yml,test2.yml,test3.yml -f test4.yml --filename test5.yml actions.github.com/k8s-deploy={"run":"3498366832","repository":"jaiveerk/k8s-deploy","workflow":"Minikube Integration Tests - private cluster","workflowFileName":"run-integration-tests-private.yml","jobName":"run-integration-test","createdBy":"jaiveerk","runUri":"https://github.com/jaiveerk/k8s-deploy/actions/runs/3498366832","commit":"c63b323186ea1320a31290de6dcc094c06385e75","lastSuccessRunCommit":"NA","branch":"refs/heads/main","deployTimestamp":1668787848577,"dockerfilePaths":{"nginx:1.14.2":""},"manifestsPaths":["https://github.com/jaiveerk/k8s-deploy/blob/c63b323186ea1320a31290de6dcc094c06385e75/test/integration/manifests/test.yml"],"helmChartPaths":[],"provider":"GitHub"} --overwrite --namespace test-3498366832`
const mockKube = new PrivateKubectl( const mockKube = new PrivateKubectl('')
'kubectlPath',
'namespace',
true,
'resourceGroup',
'resourceName'
)
it('should extract filenames correctly', () => { it('should extract filenames correctly', () => {
expect(mockKube.extractFilesnames(testString)).toEqual( expect(mockKube.extractFilesnames(testString)).toEqual(
'test.yml test2.yml test3.yml test4.yml test5.yml' 'test.yml test2.yml test3.yml test4.yml test5.yml'
) )
}) })
test('Should throw well defined Error on error from Azure', async () => {
const errorMsg = 'An error message'
jest.spyOn(exec, 'getExecOutput').mockImplementation(async () => {
return {exitCode: 1, stdout: '', stderr: errorMsg}
})
await expect(mockKube.executeCommand('az', 'test')).rejects.toThrow(
Error(
`Call to private cluster failed. Command: 'kubectl az test --insecure-skip-tls-verify --namespace namespace', errormessage: ${errorMsg}`
)
)
})
}) })
+3 -8
View File
@@ -8,6 +8,8 @@ import * as path from 'path'
export class PrivateKubectl extends Kubectl { export class PrivateKubectl extends Kubectl {
protected async execute(args: string[], silent: boolean = false) { protected async execute(args: string[], silent: boolean = false) {
args = args.concat(this.getExecuteFlags())
args.unshift('kubectl') args.unshift('kubectl')
let kubectlCmd = args.join(' ') let kubectlCmd = args.join(' ')
let addFileFlag = false let addFileFlag = false
@@ -73,18 +75,11 @@ export class PrivateKubectl extends Kubectl {
runOutput runOutput
)}` )}`
) )
if (runOutput.exitCode !== 0) {
throw Error(
`Call to private cluster failed. Command: '${kubectlCmd}', errormessage: ${runOutput.stderr}`
)
}
const runObj: {logs: string; exitCode: number} = JSON.parse( const runObj: {logs: string; exitCode: number} = JSON.parse(
runOutput.stdout runOutput.stdout
) )
if (!silent) core.info(runObj.logs) if (!silent) core.info(runObj.logs)
if (runObj.exitCode !== 0) { if (runOutput.exitCode !== 0 && runObj.exitCode !== 0) {
throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`) throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`)
} }
+3 -12
View File
@@ -2,8 +2,6 @@ import * as core from '@actions/core'
import {ExecOutput} from '@actions/exec' import {ExecOutput} from '@actions/exec'
import {Kubectl} from '../types/kubectl' import {Kubectl} from '../types/kubectl'
const NAMESPACE = 'namespace'
export function checkForErrors( export function checkForErrors(
execResults: ExecOutput[], execResults: ExecOutput[],
warnIfError?: boolean warnIfError?: boolean
@@ -32,12 +30,7 @@ export async function getLastSuccessfulRunSha(
annotationKey: string annotationKey: string
): Promise<string> { ): Promise<string> {
try { try {
const result = await kubectl.getResource( const result = await kubectl.getResource('namespace', namespaceName)
NAMESPACE,
namespaceName,
false,
namespaceName
)
if (result?.stderr) { if (result?.stderr) {
core.warning(result.stderr) core.warning(result.stderr)
return process.env.GITHUB_SHA return process.env.GITHUB_SHA
@@ -60,13 +53,12 @@ export async function annotateChildPods(
kubectl: Kubectl, kubectl: Kubectl,
resourceType: string, resourceType: string,
resourceName: string, resourceName: string,
namespace: string | undefined,
annotationKeyValStr: string, annotationKeyValStr: string,
allPods allPods
): Promise<ExecOutput[]> { ): Promise<ExecOutput[]> {
let owner = resourceName let owner = resourceName
if (resourceType.toLowerCase().indexOf('deployment') > -1) { if (resourceType.toLowerCase().indexOf('deployment') > -1) {
owner = await kubectl.getNewReplicaSet(resourceName, namespace) owner = await kubectl.getNewReplicaSet(resourceName)
} }
const commandExecutionResults = [] const commandExecutionResults = []
@@ -80,8 +72,7 @@ export async function annotateChildPods(
kubectl.annotate( kubectl.annotate(
'pod', 'pod',
pod.metadata.name, pod.metadata.name,
annotationKeyValStr, annotationKeyValStr
namespace
) )
) )
break break
+28 -51
View File
@@ -4,9 +4,6 @@ import {Kubectl, Resource} from '../types/kubectl'
import {checkForErrors} from './kubectlUtils' import {checkForErrors} from './kubectlUtils'
import {sleep} from './timeUtils' import {sleep} from './timeUtils'
const IS_SILENT = false
const POD = 'pod'
export async function checkManifestStability( export async function checkManifestStability(
kubectl: Kubectl, kubectl: Kubectl,
resources: Resource[] resources: Resource[]
@@ -23,35 +20,24 @@ export async function checkManifestStability(
try { try {
const result = await kubectl.checkRolloutStatus( const result = await kubectl.checkRolloutStatus(
resource.type, resource.type,
resource.name, resource.name
resource.namespace
) )
checkForErrors([result]) checkForErrors([result])
} catch (ex) { } catch (ex) {
core.error(ex) core.error(ex)
await kubectl.describe( await kubectl.describe(resource.type, resource.name)
resource.type,
resource.name,
IS_SILENT,
resource.namespace
)
rolloutStatusHasErrors = true rolloutStatusHasErrors = true
} }
} }
if (resource.type == KubernetesConstants.KubernetesWorkload.POD) { if (resource.type == KubernetesConstants.KubernetesWorkload.POD) {
try { try {
await checkPodStatus(kubectl, resource) await checkPodStatus(kubectl, resource.name)
} catch (ex) { } catch (ex) {
core.warning( core.warning(
`Could not determine pod status: ${JSON.stringify(ex)}` `Could not determine pod status: ${JSON.stringify(ex)}`
) )
await kubectl.describe( await kubectl.describe(resource.type, resource.name)
resource.type,
resource.name,
IS_SILENT,
resource.namespace
)
} }
} }
if ( if (
@@ -59,11 +45,14 @@ export async function checkManifestStability(
KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE
) { ) {
try { try {
const service = await getService(kubectl, resource) const service = await getService(kubectl, resource.name)
const {spec, status} = service const {spec, status} = service
if (spec.type === KubernetesConstants.ServiceTypes.LOAD_BALANCER) { if (spec.type === KubernetesConstants.ServiceTypes.LOAD_BALANCER) {
if (!isLoadBalancerIPAssigned(status)) { if (!isLoadBalancerIPAssigned(status)) {
await waitForServiceExternalIPAssignment(kubectl, resource) await waitForServiceExternalIPAssignment(
kubectl,
resource.name
)
} else { } else {
core.info( core.info(
`ServiceExternalIP ${resource.name} ${status.loadBalancer.ingress[0].ip}` `ServiceExternalIP ${resource.name} ${status.loadBalancer.ingress[0].ip}`
@@ -74,12 +63,7 @@ export async function checkManifestStability(
core.warning( core.warning(
`Could not determine service status of: ${resource.name} Error: ${ex}` `Could not determine service status of: ${resource.name} Error: ${ex}`
) )
await kubectl.describe( await kubectl.describe(resource.type, resource.name)
resource.type,
resource.name,
IS_SILENT,
resource.namespace
)
} }
} }
} }
@@ -91,7 +75,7 @@ export async function checkManifestStability(
export async function checkPodStatus( export async function checkPodStatus(
kubectl: Kubectl, kubectl: Kubectl,
pod: Resource podName: string
): Promise<void> { ): Promise<void> {
const sleepTimeout = 10 * 1000 // 10 seconds const sleepTimeout = 10 * 1000 // 10 seconds
const iterations = 60 // 60 * 10 seconds timeout = 10 minutes max timeout const iterations = 60 // 60 * 10 seconds timeout = 10 minutes max timeout
@@ -101,8 +85,8 @@ export async function checkPodStatus(
for (let i = 0; i < iterations; i++) { for (let i = 0; i < iterations; i++) {
await sleep(sleepTimeout) await sleep(sleepTimeout)
core.debug(`Polling for pod status: ${pod.name}`) core.debug(`Polling for pod status: ${podName}`)
podStatus = await getPodStatus(kubectl, pod) podStatus = await getPodStatus(kubectl, podName)
if ( if (
podStatus && podStatus &&
@@ -113,42 +97,37 @@ export async function checkPodStatus(
} }
} }
podStatus = await getPodStatus(kubectl, pod) podStatus = await getPodStatus(kubectl, podName)
switch (podStatus.phase) { switch (podStatus.phase) {
case 'Succeeded': case 'Succeeded':
case 'Running': case 'Running':
if (isPodReady(podStatus)) { if (isPodReady(podStatus)) {
console.log(`pod/${pod.name} is successfully rolled out`) console.log(`pod/${podName} is successfully rolled out`)
} else { } else {
kubectlDescribeNeeded = true kubectlDescribeNeeded = true
} }
break break
case 'Pending': case 'Pending':
if (!isPodReady(podStatus)) { if (!isPodReady(podStatus)) {
core.warning(`pod/${pod.name} rollout status check timed out`) core.warning(`pod/${podName} rollout status check timed out`)
kubectlDescribeNeeded = true kubectlDescribeNeeded = true
} }
break break
case 'Failed': case 'Failed':
core.error(`pod/${pod.name} rollout failed`) core.error(`pod/${podName} rollout failed`)
kubectlDescribeNeeded = true kubectlDescribeNeeded = true
break break
default: default:
core.warning(`pod/${pod.name} rollout status: ${podStatus.phase}`) core.warning(`pod/${podName} rollout status: ${podStatus.phase}`)
} }
if (kubectlDescribeNeeded) { if (kubectlDescribeNeeded) {
await kubectl.describe(POD, pod.name, IS_SILENT, pod.namespace) await kubectl.describe('pod', podName)
} }
} }
async function getPodStatus(kubectl: Kubectl, pod: Resource) { async function getPodStatus(kubectl: Kubectl, podName: string) {
const podResult = await kubectl.getResource( const podResult = await kubectl.getResource('pod', podName)
POD,
pod.name,
IS_SILENT,
pod.namespace
)
checkForErrors([podResult]) checkForErrors([podResult])
return JSON.parse(podResult.stdout).status return JSON.parse(podResult.stdout).status
@@ -172,12 +151,10 @@ function isPodReady(podStatus: any): boolean {
return allContainersAreReady return allContainersAreReady
} }
async function getService(kubectl: Kubectl, service: Resource) { async function getService(kubectl: Kubectl, serviceName) {
const serviceResult = await kubectl.getResource( const serviceResult = await kubectl.getResource(
KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE, KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE,
service.name, serviceName
IS_SILENT,
service.namespace
) )
checkForErrors([serviceResult]) checkForErrors([serviceResult])
@@ -186,25 +163,25 @@ async function getService(kubectl: Kubectl, service: Resource) {
async function waitForServiceExternalIPAssignment( async function waitForServiceExternalIPAssignment(
kubectl: Kubectl, kubectl: Kubectl,
service: Resource serviceName: string
): Promise<void> { ): Promise<void> {
const sleepTimeout = 10 * 1000 // 10 seconds const sleepTimeout = 10 * 1000 // 10 seconds
const iterations = 18 // 18 * 10 seconds timeout = 3 minutes max timeout const iterations = 18 // 18 * 10 seconds timeout = 3 minutes max timeout
for (let i = 0; i < iterations; i++) { for (let i = 0; i < iterations; i++) {
core.info(`Wait for service ip assignment : ${service.name}`) core.info(`Wait for service ip assignment : ${serviceName}`)
await sleep(sleepTimeout) await sleep(sleepTimeout)
const status = (await getService(kubectl, service)).status const status = (await getService(kubectl, serviceName)).status
if (isLoadBalancerIPAssigned(status)) { if (isLoadBalancerIPAssigned(status)) {
core.info( core.info(
`ServiceExternalIP ${service.name} ${status.loadBalancer.ingress[0].ip}` `ServiceExternalIP ${serviceName} ${status.loadBalancer.ingress[0].ip}`
) )
return return
} }
} }
core.warning(`Wait for service ip assignment timed out ${service.name}`) core.warning(`Wait for service ip assignment timed out${serviceName}`)
} }
function isLoadBalancerIPAssigned(status: any) { function isLoadBalancerIPAssigned(status: any) {
+1 -2
View File
@@ -280,8 +280,7 @@ export function getResources(
) { ) {
resources.push({ resources.push({
type: inputObject.kind, type: inputObject.kind,
name: inputObject.metadata.name, name: inputObject.metadata.name
namespace: inputObject?.metadata?.namespace
}) })
} }
}) })
+1 -14
View File
@@ -1,8 +1,4 @@
import { import {cleanLabel} from '../utilities/workflowAnnotationUtils'
cleanLabel,
removeInvalidLabelCharacters,
VALID_LABEL_REGEX
} from '../utilities/workflowAnnotationUtils'
describe('WorkflowAnnotationUtils', () => { describe('WorkflowAnnotationUtils', () => {
describe('cleanLabel', () => { describe('cleanLabel', () => {
@@ -20,14 +16,5 @@ describe('WorkflowAnnotationUtils', () => {
cleanLabel('Workflow Name / With Slashes / And Spaces') cleanLabel('Workflow Name / With Slashes / And Spaces')
).toEqual('Workflow_Name_-_With_Slashes_-_And_Spaces') ).toEqual('Workflow_Name_-_With_Slashes_-_And_Spaces')
}) })
it('should return a blank string when regex fails (https://github.com/Azure/k8s-deploy/issues/266)', () => {
const label = '持续部署'
expect(cleanLabel(label)).toEqual('github-workflow-file')
let removedInvalidChars = removeInvalidLabelCharacters(label)
const regexResult = VALID_LABEL_REGEX.exec(removedInvalidChars)
expect(regexResult).toBe(null)
})
}) })
}) })
+4 -12
View File
@@ -2,8 +2,6 @@ import {DeploymentConfig} from '../types/deploymentConfig'
const ANNOTATION_PREFIX = 'actions.github.com' const ANNOTATION_PREFIX = 'actions.github.com'
export const VALID_LABEL_REGEX = /([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]/
export function getWorkflowAnnotations( export function getWorkflowAnnotations(
lastSuccessRunSha: string, lastSuccessRunSha: string,
workflowFilePath: string, workflowFilePath: string,
@@ -39,17 +37,11 @@ export function getWorkflowAnnotationKeyLabel(): string {
* @returns cleaned label * @returns cleaned label
*/ */
export function cleanLabel(label: string): string { export function cleanLabel(label: string): string {
let removedInvalidChars = removeInvalidLabelCharacters(label) let removedInvalidChars = label
const regexResult = VALID_LABEL_REGEX.exec(removedInvalidChars) || [
'github-workflow-file'
]
return regexResult[0]
}
export function removeInvalidLabelCharacters(label: string): string {
return label
.replace(/\s/gi, '_') .replace(/\s/gi, '_')
.replace(/[\/\\\|]/gi, '-') .replace(/[\/\\\|]/gi, '-')
.replace(/[^-A-Za-z0-9_.]/gi, '') .replace(/[^-A-Za-z0-9_.]/gi, '')
const regex = /([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]/
return regex.exec(removedInvalidChars)[0] || ''
} }