Compare commits

..

39 Commits

Author SHA1 Message Date
Oliver King ff21bd2d58 Add node modules and compiled JavaScript from main 2023-05-17 14:49:42 +00:00
Oliver King 172f1e16bd Merge branch 'releases/v4' into tmp 2023-05-17 14:49:26 +00:00
Olivier Tétard e52890db9e Fix “Service” route-method of the Blue-Green strategy with some manifest files (#283) 2023-04-17 13:52:50 -04:00
David Gamero dd4bbd13a5 bump codeql to node 16 (#281)
* upgrade codeql

* bump codeql init

* name the unit test job

* tats feedback
2023-02-22 16:05:36 -05:00
Oliver King ecb488266d Fixes multiple namespaces bug (#276)
* fix ns bug

* add tests

* rename some variables

* rename ns to namespace

* fix delete + correctly type

* add typing to input obj parser
2023-02-06 13:42:55 -05:00
David Gamero 756cc0a511 upgrade codeql (#279) 2023-01-31 16:43:21 -05:00
Thomas Oddsund dcaec012e2 Check for error from Azure when using the private-cluster feature (#270)
* Check for error from Azure

Move the error check for Azure earlier, so that a well defined error is
thrown on error instead of a JSONSyntax error.

The issue is that when Azure returns an error, like when there's an
issue with the access to the principal used. When this happens, the
stdout field will be an empty string, and the error message will be set.

* Restore check for deserialized exitCode
2023-01-03 10:09:55 -05:00
David Gamero 7dae909398 abstract methods to avoid drift (#273) 2022-12-19 17:59:02 -05:00
Jaiveer Katariya e8a841df59 Fixing Regex Issue + Adding Check for Failures Connecting to Github Repos (#271)
* changed ubuntu runner

* changed minikube action

* Version formatting

* nonedriveR

* update kube version

* installing conntrack'

* updated other actions

* update bg ingress api version

* prettify

* updated ingress backend for new api version

* Added path type

* prettify

* added logging

* added try catch logic to prevent future failures if annotations fail since failing annotations shouldn't affect users

* added nullcheck

* Added fallback filename if workflow fails to get github filepath due to runner issues

* cleanup

* added oliver's feedback + unit test demonstrating regex glitch and fix

* no longer using blank string for failed regex
2022-12-14 08:18:16 -05:00
Oliver King da1e907ad7 Update README.md to v4 (#263) 2022-12-05 18:23:38 -05:00
Jaiveer Katariya 8ce7d1dcdd fixed files to file (#265) 2022-11-29 15:30:17 -05:00
Oliver King b9a9965750 bump @actions/core (#262) 2022-11-28 09:19:53 -05:00
Oliver King 47445fb82f Add skip tls flag (#260) 2022-11-23 12:59:45 -05:00
Jaiveer Katariya c875a14bde Fixing Ubuntu Runner Issue (#259)
* changed ubuntu runner

* changed minikube action

* Version formatting

* nonedriveR

* update kube version

* installing conntrack'

* updated other actions

* update bg ingress api version

* prettify

* updated ingress backend for new api version

* Added path type

* prettify
2022-11-23 09:25:05 -05:00
Jaiveer Katariya 58ba3f0665 new commit with all changes (#258) 2022-11-21 10:30:35 -05:00
Jaiveer Katariya e9693a7cdd Added Integration Tests, Resolved Bugs With Annotations (#255)
* First commit - made manifests for test deployments, made manifests for i tests for other deployment strategies

* broke down blue/green

* added latest tags to test manifests for new tags

* remade tester

* ready to test bgi

* using all but first index of argv

* careless error with dicts

* added test to namespace

* realized i was silencing error

* indexing containers

* keyerror

* logging bc python errors are weird

* expected still string

* parsed args behaving weirdly

* test seems to be working now, applying changes to other YAMLs now

* blue/green ready to test

* oops

* oops

* Added additional labels to check

* hyphen

* Added our annotations

* lol

* added our labels to services too

* nonetype issue'

* nonetype issue'

* narrowing down parameter

* fixed annotations issue with promote

* adding debhug statement to figure out why services aren't getting annotations

* this should fix annotations issue for service

* not sure why this wasn't caught by intellisense

* should be fixed with removing comma but adding logs in case

* added linkerd install

* verification

* upgraded kubernetes version

* removing crds

* proxy option

* Added smi extension

* logging service

* smi svcs also getting labeled now

* matching ts type

* not sure where stable service is going

* remaining svc and deployment should match

* keeping stable service and ts object

* updated tests to reflect keeping ts object

* no green svc after promote

* duh

* lol

* canary work

* canary test ready

* logging for ing, filename for canary

* changed ingress svc key and returning svc files from smi canary deployment

* ts name

* forgot about baseline in first deploy

* *

* *

* smi canary should annotate, fixed cleanup

* typescript issue plus percentage

* forgot to type extra method

* removed cleaned up objects from annotate list

* logging because services aren't getting removed

* moving to try/catch strategy of annotation since deletion can fail silently/with warnings

* moved label to individual

* removing canary service check after promote

* pod ready for testing

* set weights to 1000

* selectors

* *

* percentage

* *

* typing

* mixed up pod and smi

* fixed tests

* prettier

* forgot to remove canary

* cleanup

* Added oliver's feedback + more cleanup

* ncc as dev dependency

* npx

* going back to global ncc install bc npm is being weird

* prettier

* removed unnecessary post step
2022-11-01 16:02:57 -04:00
Oliver King a6cfc31f7a Fix private cluster kubectl exit code bug (#252)
* add private cluster exitCode check

* add proper output
2022-10-19 13:27:21 -04:00
Jaiveer Katariya e917b5a666 Deploy with Manifests from URLs (#251)
* added functionality, need to add/modify existing tests

* added tests

* updated readme

* prettier
2022-10-17 17:48:28 -04:00
Asa Gayle 57d0489e1f Added support message (#249) 2022-10-17 14:01:46 -04:00
Jaiveer Katariya d64c205796 Resolved issue with Canary deploy (#247) 2022-10-14 12:25:27 -04:00
Kenta Nakase c8f050230d Fix description about baseline-and-canary-replicas (#241) 2022-09-28 14:21:08 -04:00
Kenta Nakase a0b037b13e Fix issue form (#238) 2022-09-15 11:23:38 -04:00
Vidya Reddy 7fd0e52a8b Add the bug report and feature request form (#237)
* Added the bug report and feature request form

* updated the url
2022-09-06 13:10:29 -04:00
Oliver King 659bbb3802 Add permissions to README.md (#236)
* Add permissions to README.md

* remove space

* prettier

* remove extra changes

* fix spacing
2022-08-31 10:19:52 -04:00
dependabot[bot] 3c0579b484 Bump @actions/core from 1.9.0 to 1.9.1 (#233)
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.9.0 to 1.9.1.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-22 14:00:31 -04:00
Oliver King b11eda66ea Fix README.md typo (#235) 2022-08-22 11:07:47 -04:00
Alexander Bartsch c117b29f9e consider slashes while cleaning labels (#231)
fix prettier format check errors
2022-08-16 14:28:12 -04:00
Jaiveer Katariya 01a65512ea Blue/Green Refactor (#229)
* fresh new branch

* Added coverage to gitignore

Signed-off-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>

* reverted package-lock.json

Signed-off-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>
Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>
2022-08-12 15:47:05 -04:00
Jaiveer Katariya 531cfdcc3d Fixed Blue/Green Strategy Ingress Route-Method Glitch (#217)
* Added some tests, not sure what else to try but gonna think of more examples

* forgot some files

* reverted package-lock.json

* Added empty dir test

* Cleaned up some extra spaces

* Add node modules and compiled JavaScript from main

* forgot to actually include functionality

* removed unnecessary files

* Update .gitignore

* Update .gitignore

* Update .gitignore

* thx david

* renamed searchFilesRec

* integrations test fix

* added examples to README

* added note about depth

* added additional note

* removed ticks

* changed version string

* removed conflict on readme

* Added tests for bluegreen helper and resolved issue with ingress not being read correctly, still have to figure out why new services aren't showing up

* resolved services name issue

* looks functional, beginning refactor now

* refactored deploy methods for type error

* Removed refactor comments

* prettier

* implemented Oliver's feedback

* prettier

* added optional chaining operator

* removed refactor comment

Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MacBook-Pro.local>
Co-authored-by: Oliver King <oking3@uncc.edu>
Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>
2022-07-29 10:58:58 -04:00
Marcus-Hines 0b5795551a Private Cluster functionality (#214) 2022-07-28 17:14:02 -04:00
Vidya Reddy bb0278db72 Swap annotation key to actions.github.com prefix (#216) 2022-07-27 13:53:57 -04:00
Vidya Reddy 71e93a71d4 Added Traffic split annotations (#215)
* Added Traffic split annotations

* traffic split - blueGreen deployment

* traffic split - canary deployment

* Traffic split annotations - canary deployment

* updated Readme and action.yml

* Traffic split - canary deployment

* clean code

* Clean code

* Clean code

* Create annotation object

* Updated Readme and action.yml

* Spelling correction

Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com>
2022-07-25 13:43:13 -04:00
Oliver King 19d66d6bdb add clean function (#211) 2022-07-06 16:15:31 -04:00
Hariharan Subramanian 72a09f4051 Logging Changes for Promote, Reject actions (#207) 2022-07-06 10:41:48 -04:00
Vidya Reddy a17f35ba63 Add ncc build to build script (#208)
Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com>
2022-07-05 10:16:41 -07:00
Hariharan Subramanian 7b11ddb1d5 Hari/beautify logs (#206)
* Logging changes for deploy

* Logging Changes with group

* format check changes
2022-06-29 11:26:44 -04:00
David Gamero ecec5912ba switch none deployment strategy to basic (#204)
* switch none deployment strategy to basic

* update readme

* update deployment strategy fallthrough logic

* comment fixed

* add disclaimer for basic strategy only supporting deploy action
2022-06-28 16:33:13 -04:00
Vidya dcd9bc6b1a Vidya reddy/prettier code (#203) 2022-06-24 16:57:45 -04:00
nv35 976c5c4981 Add missing API switch for GHES (#200) 2022-06-22 12:14:43 -04:00
20 changed files with 494 additions and 398 deletions
+4 -9
View File
@@ -13,20 +13,15 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v3
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@v1 uses: github/codeql-action/init@v2
# 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
@@ -34,7 +29,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@v1 uses: github/codeql-action/autobuild@v2
# ️ 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
@@ -48,4 +43,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1 uses: github/codeql-action/analyze@v2
+2 -1
View File
@@ -11,9 +11,10 @@ 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@v1 - uses: actions/checkout@v3
- run: | - run: |
npm install npm install
npm test npm test
+102 -86
View File
@@ -20210,7 +20210,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); yield kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.INGRESS, ingressResource.name, false, ingressResource.namespace);
} }
core.endGroup(); core.endGroup();
// annotate resources // annotate resources
@@ -20593,7 +20593,8 @@ 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)}`);
@@ -20620,28 +20621,24 @@ 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)) {
if (isServiceRouted(inputObject, deploymentEntityList)) { serviceEntityList.push(inputObject);
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);
@@ -20652,6 +20649,16 @@ 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,
@@ -20746,9 +20753,9 @@ function isServiceSelectorSubsetOfMatchLabel(serviceSelector, matchLabels) {
return isMatch; return isMatch;
} }
exports.isServiceSelectorSubsetOfMatchLabel = isServiceSelectorSubsetOfMatchLabel; exports.isServiceSelectorSubsetOfMatchLabel = isServiceSelectorSubsetOfMatchLabel;
function fetchResource(kubectl, kind, name) { function fetchResource(kubectl, kind, name, namespace) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const result = yield kubectl.getResource(kind, name); const result = yield kubectl.getResource(kind, name, false, namespace);
if (result == null || !!result.stderr) { if (result == null || !!result.stderr) {
return null; return null;
} }
@@ -20948,16 +20955,16 @@ function isIngressRouted(ingressObject, serviceNameMap) {
} }
exports.isIngressRouted = isIngressRouted; exports.isIngressRouted = isIngressRouted;
function validateIngresses(kubectl, ingressEntityList, serviceNameMap) { function validateIngresses(kubectl, ingressEntityList, serviceNameMap) {
var _a; var _a, _b;
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); 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 isValid = !!existingIngress && const isValid = !!existingIngress &&
((_a = existingIngress === null || existingIngress === void 0 ? void 0 : existingIngress.metadata) === null || _a === void 0 ? void 0 : _a.labels[blueGreenHelper_1.BLUE_GREEN_VERSION_LABEL]) === ((_b = existingIngress === null || existingIngress === void 0 ? void 0 : existingIngress.metadata) === null || _b === void 0 ? void 0 : _b.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)}`);
@@ -21215,11 +21222,12 @@ 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); 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);
let isServiceGreen = !!existingService && let isServiceGreen = !!existingService &&
getServiceSpecLabel(existingService) == getServiceSpecLabel(existingService) ==
blueGreenHelper_1.GREEN_LABEL_VALUE; blueGreenHelper_1.GREEN_LABEL_VALUE;
@@ -21340,11 +21348,12 @@ 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)); 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);
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}`);
@@ -21371,9 +21380,11 @@ 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
@@ -21537,11 +21548,12 @@ 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) { const deleteObject = function (kind, name, namespace) {
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]); const result = yield kubectl.delete([kind, name], namespace);
kubectlUtils_1.checkForErrors([result]); kubectlUtils_1.checkForErrors([result]);
} }
catch (ex) { catch (ex) {
@@ -21556,13 +21568,14 @@ 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); yield deleteObject(kind, canaryObjectName, namespace);
yield deleteObject(kind, baselineObjectName); yield deleteObject(kind, baselineObjectName, namespace);
} }
} }
} }
@@ -22053,7 +22066,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)); annotateResults.push(yield kubectl.annotate('namespace', namespace, annotationKeyValStr, namespace));
} }
for (const file of files) { for (const file of files) {
try { try {
@@ -22068,7 +22081,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, annotationKeyValStr, allPods)).forEach((execResult) => annotateResults.push(execResult)); (yield kubectlUtils_1.annotateChildPods(kubectl, resource.type, resource.name, resource.namespace, annotationKeyValStr, allPods)).forEach((execResult) => annotateResults.push(execResult));
} }
} }
kubectlUtils_1.checkForErrors(annotateResults, true); kubectlUtils_1.checkForErrors(annotateResults, true);
@@ -22331,7 +22344,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 = 'default', ignoreSSLErrors = false, resourceGroup = '', name = '') { constructor(kubectlPath, namespace = '', ignoreSSLErrors = false, resourceGroup = '', name = '') {
this.kubectlPath = kubectlPath; this.kubectlPath = kubectlPath;
this.ignoreSSLErrors = !!ignoreSSLErrors; this.ignoreSSLErrors = !!ignoreSSLErrors;
this.namespace = namespace; this.namespace = namespace;
@@ -22350,21 +22363,21 @@ class Kubectl {
]; ];
if (force) if (force)
applyArgs.push('--force'); applyArgs.push('--force');
return yield this.execute(applyArgs); return yield this.execute(applyArgs.concat(this.getFlags()));
} }
catch (err) { catch (err) {
core.debug('Kubectl apply failed:' + err); core.debug('Kubectl apply failed:' + err);
} }
}); });
} }
describe(resourceType, resourceName, silent = false) { describe(resourceType, resourceName, silent = false, namespace) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return yield this.execute(['describe', resourceType, resourceName], silent); return yield this.execute(['describe', resourceType, resourceName].concat(this.getFlags(namespace)), silent);
}); });
} }
getNewReplicaSet(deployment) { getNewReplicaSet(deployment, namespace) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const result = yield this.describe('deployment', deployment, true); const result = yield this.describe('deployment', deployment, true, namespace);
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');
@@ -22384,7 +22397,7 @@ class Kubectl {
return newReplicaSet; return newReplicaSet;
}); });
} }
annotate(resourceType, resourceName, annotation) { annotate(resourceType, resourceName, annotation, namespace) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const args = [ const args = [
'annotate', 'annotate',
@@ -22392,11 +22405,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) { annotateFiles(files, annotation, namespace) {
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}`);
@@ -22406,12 +22419,11 @@ 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) { labelFiles(files, labels, namespace) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const args = [ const args = [
'label', 'label',
@@ -22419,56 +22431,53 @@ 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'], true); return yield this.execute(['get', 'pods', '-o', 'json'].concat(this.getFlags()), true);
}); });
} }
checkRolloutStatus(resourceType, name) { checkRolloutStatus(resourceType, name, namespace) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return yield this.execute([ return yield this.execute(['rollout', 'status', `${resourceType}/${name}`].concat(this.getFlags(namespace)));
'rollout',
'status',
`${resourceType}/${name}`
]);
}); });
} }
getResource(resourceType, name, silentFailure = false) { getResource(resourceType, name, silentFailure = false, namespace) {
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'], silentFailure); return yield this.execute(['get', `${resourceType}/${name}`, '-o', 'json'].concat(this.getFlags(namespace)), 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');
return args ? this.execute([command, args]) : this.execute([command]); const a = args ? [args] : [];
return this.execute([command, ...a.concat(this.getFlags())]);
} }
delete(args) { delete(args, namespace) {
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))]);
} }
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
}); });
}); });
} }
getExecuteFlags() { getFlags(namespaceOverride) {
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;
} }
@@ -22599,7 +22608,6 @@ 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;
@@ -22649,10 +22657,13 @@ 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 (runOutput.exitCode !== 0 && runObj.exitCode !== 0) { if (runObj.exitCode !== 0) {
throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`); throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`);
} }
return { return {
@@ -23160,6 +23171,7 @@ 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) => {
@@ -23183,7 +23195,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); const result = yield kubectl.getResource(NAMESPACE, namespaceName, false, 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;
@@ -23206,12 +23218,12 @@ function getLastSuccessfulRunSha(kubectl, namespaceName, annotationKey) {
}); });
} }
exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha; exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha;
function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyValStr, allPods) { function annotateChildPods(kubectl, resourceType, resourceName, namespace, 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); owner = yield kubectl.getNewReplicaSet(resourceName, namespace);
} }
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) {
@@ -23221,7 +23233,7 @@ function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyVal
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)); commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, annotationKeyValStr, namespace));
break; break;
} }
} }
@@ -23376,6 +23388,8 @@ 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;
@@ -23383,32 +23397,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); const result = yield kubectl.checkRolloutStatus(resource.type, resource.name, resource.namespace);
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); yield kubectl.describe(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 {
yield checkPodStatus(kubectl, resource.name); yield checkPodStatus(kubectl, resource);
} }
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); yield kubectl.describe(resource.type, resource.name, IS_SILENT, resource.namespace);
} }
} }
if (resource.type == if (resource.type ==
KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE) { KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE) {
try { try {
const service = yield getService(kubectl, resource.name); const service = yield getService(kubectl, resource);
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.name); yield waitForServiceExternalIPAssignment(kubectl, resource);
} }
else { else {
core.info(`ServiceExternalIP ${resource.name} ${status.loadBalancer.ingress[0].ip}`); core.info(`ServiceExternalIP ${resource.name} ${status.loadBalancer.ingress[0].ip}`);
@@ -23417,7 +23431,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); yield kubectl.describe(resource.type, resource.name, IS_SILENT, resource.namespace);
} }
} }
} }
@@ -23427,7 +23441,7 @@ function checkManifestStability(kubectl, resources) {
}); });
} }
exports.checkManifestStability = checkManifestStability; exports.checkManifestStability = checkManifestStability;
function checkPodStatus(kubectl, podName) { function checkPodStatus(kubectl, pod) {
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
@@ -23435,20 +23449,20 @@ function checkPodStatus(kubectl, podName) {
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: ${podName}`); core.debug(`Polling for pod status: ${pod.name}`);
podStatus = yield getPodStatus(kubectl, podName); podStatus = yield getPodStatus(kubectl, pod);
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, podName); podStatus = yield getPodStatus(kubectl, pod);
switch (podStatus.phase) { switch (podStatus.phase) {
case 'Succeeded': case 'Succeeded':
case 'Running': case 'Running':
if (isPodReady(podStatus)) { if (isPodReady(podStatus)) {
console.log(`pod/${podName} is successfully rolled out`); console.log(`pod/${pod.name} is successfully rolled out`);
} }
else { else {
kubectlDescribeNeeded = true; kubectlDescribeNeeded = true;
@@ -23456,26 +23470,26 @@ function checkPodStatus(kubectl, podName) {
break; break;
case 'Pending': case 'Pending':
if (!isPodReady(podStatus)) { if (!isPodReady(podStatus)) {
core.warning(`pod/${podName} rollout status check timed out`); core.warning(`pod/${pod.name} rollout status check timed out`);
kubectlDescribeNeeded = true; kubectlDescribeNeeded = true;
} }
break; break;
case 'Failed': case 'Failed':
core.error(`pod/${podName} rollout failed`); core.error(`pod/${pod.name} rollout failed`);
kubectlDescribeNeeded = true; kubectlDescribeNeeded = true;
break; break;
default: default:
core.warning(`pod/${podName} rollout status: ${podStatus.phase}`); core.warning(`pod/${pod.name} rollout status: ${podStatus.phase}`);
} }
if (kubectlDescribeNeeded) { if (kubectlDescribeNeeded) {
yield kubectl.describe('pod', podName); yield kubectl.describe(POD, pod.name, IS_SILENT, pod.namespace);
} }
}); });
} }
exports.checkPodStatus = checkPodStatus; exports.checkPodStatus = checkPodStatus;
function getPodStatus(kubectl, podName) { function getPodStatus(kubectl, pod) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const podResult = yield kubectl.getResource('pod', podName); const podResult = yield kubectl.getResource(POD, pod.name, IS_SILENT, pod.namespace);
kubectlUtils_1.checkForErrors([podResult]); kubectlUtils_1.checkForErrors([podResult]);
return JSON.parse(podResult.stdout).status; return JSON.parse(podResult.stdout).status;
}); });
@@ -23493,27 +23507,27 @@ function isPodReady(podStatus) {
} }
return allContainersAreReady; return allContainersAreReady;
} }
function getService(kubectl, serviceName) { function getService(kubectl, service) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const serviceResult = yield kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE, serviceName); const serviceResult = yield kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE, service.name, IS_SILENT, service.namespace);
kubectlUtils_1.checkForErrors([serviceResult]); kubectlUtils_1.checkForErrors([serviceResult]);
return JSON.parse(serviceResult.stdout); return JSON.parse(serviceResult.stdout);
}); });
} }
function waitForServiceExternalIPAssignment(kubectl, serviceName) { function waitForServiceExternalIPAssignment(kubectl, service) {
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 : ${serviceName}`); core.info(`Wait for service ip assignment : ${service.name}`);
yield timeUtils_1.sleep(sleepTimeout); yield timeUtils_1.sleep(sleepTimeout);
const status = (yield getService(kubectl, serviceName)).status; const status = (yield getService(kubectl, service)).status;
if (isLoadBalancerIPAssigned(status)) { if (isLoadBalancerIPAssigned(status)) {
core.info(`ServiceExternalIP ${serviceName} ${status.loadBalancer.ingress[0].ip}`); core.info(`ServiceExternalIP ${service.name} ${status.loadBalancer.ingress[0].ip}`);
return; return;
} }
} }
core.warning(`Wait for service ip assignment timed out${serviceName}`); core.warning(`Wait for service ip assignment timed out ${service.name}`);
}); });
} }
function isLoadBalancerIPAssigned(status) { function isLoadBalancerIPAssigned(status) {
@@ -23722,11 +23736,13 @@ 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
}); });
} }
}); });
+16 -208
View File
@@ -22,8 +22,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"
@@ -1183,6 +1183,15 @@
"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",
@@ -1875,15 +1884,6 @@
"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,15 +1978,6 @@
"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",
@@ -4091,58 +4082,6 @@
"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",
@@ -5883,15 +5822,6 @@
"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",
@@ -5984,33 +5914,6 @@
"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",
@@ -6127,12 +6030,6 @@
"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",
@@ -7493,6 +7390,12 @@
"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",
@@ -8020,12 +7923,6 @@
"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",
@@ -8107,12 +8004,6 @@
"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",
@@ -9728,54 +9619,6 @@
"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",
@@ -11139,12 +10982,6 @@
"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",
@@ -11218,29 +11055,6 @@
"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",
@@ -11321,12 +11135,6 @@
"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",
+1 -1
View File
@@ -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"
+3 -1
View File
@@ -56,7 +56,9 @@ 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,7 +38,8 @@ 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
} }
}) })
@@ -66,31 +67,25 @@ 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)) {
if (isServiceRouted(inputObject, deploymentEntityList)) { serviceEntityList.push(inputObject)
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 {
@@ -100,6 +95,16 @@ 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,
@@ -234,9 +239,10 @@ 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) const result = await kubectl.getResource(kind, name, false, namespace)
if (result == null || !!result.stderr) { if (result == null || !!result.stderr) {
return null return null
} }
@@ -97,7 +97,8 @@ 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,7 +31,8 @@ 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,7 +142,8 @@ 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)}`
@@ -183,7 +184,8 @@ export async function cleanupSMI(
serviceObject.metadata.name, serviceObject.metadata.name,
GREEN_SUFFIX GREEN_SUFFIX
), ),
kind: serviceObject.kind kind: serviceObject.kind,
namespace: serviceObject?.metadata?.namespace
}) })
}) })
+9 -4
View File
@@ -195,9 +195,13 @@ async function cleanUpCanary(
files: string[], files: string[],
includeServices: boolean includeServices: boolean
): Promise<string[]> { ): Promise<string[]> {
const deleteObject = async function (kind, name) { const deleteObject = async function (
kind: string,
name: string,
namespace: string | undefined
) {
try { try {
const result = await kubectl.delete([kind, name]) const result = await kubectl.delete([kind, name], namespace)
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
@@ -213,6 +217,7 @@ 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) ||
@@ -222,8 +227,8 @@ async function cleanUpCanary(
const canaryObjectName = getCanaryResourceName(name) const canaryObjectName = getCanaryResourceName(name)
const baselineObjectName = getBaselineResourceName(name) const baselineObjectName = getBaselineResourceName(name)
await deleteObject(kind, canaryObjectName) await deleteObject(kind, canaryObjectName, namespace)
await deleteObject(kind, baselineObjectName) await deleteObject(kind, baselineObjectName, namespace)
} }
} }
} }
+7 -1
View File
@@ -218,7 +218,12 @@ async function annotateResources(
) )
if (annotateNamespace) { if (annotateNamespace) {
annotateResults.push( annotateResults.push(
await kubectl.annotate('namespace', namespace, annotationKeyValStr) await kubectl.annotate(
'namespace',
namespace,
annotationKeyValStr,
namespace
)
) )
} }
for (const file of files) { for (const file of files) {
@@ -243,6 +248,7 @@ async function annotateResources(
kubectl, kubectl,
resource.type, resource.type,
resource.name, resource.name,
resource.namespace,
annotationKeyValStr, annotationKeyValStr,
allPods allPods
) )
+2
View File
@@ -2,6 +2,7 @@ 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
@@ -16,6 +17,7 @@ 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 {
+176 -1
View File
@@ -3,7 +3,6 @@ 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'
@@ -38,6 +37,7 @@ 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,6 +122,26 @@ 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 () => {
@@ -140,6 +160,26 @@ 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 () => {
@@ -165,6 +205,27 @@ 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 () => {
@@ -185,6 +246,22 @@ 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 () => {
@@ -205,6 +282,22 @@ 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 () => {
@@ -225,6 +318,21 @@ 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 () => {
@@ -245,6 +353,21 @@ 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 () => {
@@ -273,6 +396,20 @@ 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 () => {
@@ -291,6 +428,22 @@ 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 () => {
@@ -321,6 +474,14 @@ 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 () => {
@@ -331,6 +492,14 @@ 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}
)
}) })
}) })
@@ -369,5 +538,11 @@ 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
})
}) })
}) })
+51 -33
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 = 'default', namespace: string = '',
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) return await this.execute(applyArgs.concat(this.getFlags()))
} catch (err) { } catch (err) {
core.debug('Kubectl apply failed:' + err) core.debug('Kubectl apply failed:' + err)
} }
@@ -56,16 +56,24 @@ 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], ['describe', resourceType, resourceName].concat(
this.getFlags(namespace)
),
silent silent
) )
} }
public async getNewReplicaSet(deployment: string) { public async getNewReplicaSet(deployment: string, namespace?: string) {
const result = await this.describe('deployment', deployment, true) const result = await this.describe(
'deployment',
deployment,
true,
namespace
)
let newReplicaSet = '' let newReplicaSet = ''
if (result?.stdout) { if (result?.stdout) {
@@ -94,7 +102,8 @@ 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',
@@ -102,13 +111,14 @@ 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}`)
@@ -118,16 +128,14 @@ 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',
@@ -135,51 +143,59 @@ 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(['get', 'pods', '-o', 'json'], true) return await this.execute(
['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', ['rollout', 'status', `${resourceType}/${name}`].concat(
'status', this.getFlags(namespace)
`${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'], ['get', `${resourceType}/${name}`, '-o', 'json'].concat(
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')
return args ? this.execute([command, args]) : this.execute([command]) const a = args ? [args] : []
return this.execute([command, ...a.concat(this.getFlags())])
} }
public delete(args: string | string[]) { public delete(args: string | string[], namespace?: string) {
if (typeof args === 'string') return this.execute(['delete', args]) if (typeof args === 'string')
return this.execute(['delete', ...args]) return this.execute(['delete', args].concat(this.getFlags(namespace)))
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, {
@@ -187,13 +203,15 @@ export class Kubectl {
}) })
} }
protected getExecuteFlags(): string[] { protected getFlags(namespaceOverride?: string): 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) {
flags.push('--namespace', this.namespace) const ns = namespaceOverride || this.namespace
if (ns) {
flags.push('--namespace', ns)
} }
return flags return flags
+21 -1
View File
@@ -1,12 +1,32 @@
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}`
)
)
})
}) })
+8 -3
View File
@@ -8,8 +8,6 @@ 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
@@ -75,11 +73,18 @@ 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 (runOutput.exitCode !== 0 && runObj.exitCode !== 0) { if (runObj.exitCode !== 0) {
throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`) throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`)
} }
+12 -3
View File
@@ -2,6 +2,8 @@ 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
@@ -30,7 +32,12 @@ export async function getLastSuccessfulRunSha(
annotationKey: string annotationKey: string
): Promise<string> { ): Promise<string> {
try { try {
const result = await kubectl.getResource('namespace', namespaceName) const result = await kubectl.getResource(
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
@@ -53,12 +60,13 @@ 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) owner = await kubectl.getNewReplicaSet(resourceName, namespace)
} }
const commandExecutionResults = [] const commandExecutionResults = []
@@ -72,7 +80,8 @@ export async function annotateChildPods(
kubectl.annotate( kubectl.annotate(
'pod', 'pod',
pod.metadata.name, pod.metadata.name,
annotationKeyValStr annotationKeyValStr,
namespace
) )
) )
break break
+51 -28
View File
@@ -4,6 +4,9 @@ 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[]
@@ -20,24 +23,35 @@ 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(resource.type, resource.name) await kubectl.describe(
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.name) await checkPodStatus(kubectl, resource)
} 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(resource.type, resource.name) await kubectl.describe(
resource.type,
resource.name,
IS_SILENT,
resource.namespace
)
} }
} }
if ( if (
@@ -45,14 +59,11 @@ export async function checkManifestStability(
KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE
) { ) {
try { try {
const service = await getService(kubectl, resource.name) const service = await getService(kubectl, resource)
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( await waitForServiceExternalIPAssignment(kubectl, resource)
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}`
@@ -63,7 +74,12 @@ 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(resource.type, resource.name) await kubectl.describe(
resource.type,
resource.name,
IS_SILENT,
resource.namespace
)
} }
} }
} }
@@ -75,7 +91,7 @@ export async function checkManifestStability(
export async function checkPodStatus( export async function checkPodStatus(
kubectl: Kubectl, kubectl: Kubectl,
podName: string pod: Resource
): 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
@@ -85,8 +101,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: ${podName}`) core.debug(`Polling for pod status: ${pod.name}`)
podStatus = await getPodStatus(kubectl, podName) podStatus = await getPodStatus(kubectl, pod)
if ( if (
podStatus && podStatus &&
@@ -97,37 +113,42 @@ export async function checkPodStatus(
} }
} }
podStatus = await getPodStatus(kubectl, podName) podStatus = await getPodStatus(kubectl, pod)
switch (podStatus.phase) { switch (podStatus.phase) {
case 'Succeeded': case 'Succeeded':
case 'Running': case 'Running':
if (isPodReady(podStatus)) { if (isPodReady(podStatus)) {
console.log(`pod/${podName} is successfully rolled out`) console.log(`pod/${pod.name} 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/${podName} rollout status check timed out`) core.warning(`pod/${pod.name} rollout status check timed out`)
kubectlDescribeNeeded = true kubectlDescribeNeeded = true
} }
break break
case 'Failed': case 'Failed':
core.error(`pod/${podName} rollout failed`) core.error(`pod/${pod.name} rollout failed`)
kubectlDescribeNeeded = true kubectlDescribeNeeded = true
break break
default: default:
core.warning(`pod/${podName} rollout status: ${podStatus.phase}`) core.warning(`pod/${pod.name} rollout status: ${podStatus.phase}`)
} }
if (kubectlDescribeNeeded) { if (kubectlDescribeNeeded) {
await kubectl.describe('pod', podName) await kubectl.describe(POD, pod.name, IS_SILENT, pod.namespace)
} }
} }
async function getPodStatus(kubectl: Kubectl, podName: string) { async function getPodStatus(kubectl: Kubectl, pod: Resource) {
const podResult = await kubectl.getResource('pod', podName) const podResult = await kubectl.getResource(
POD,
pod.name,
IS_SILENT,
pod.namespace
)
checkForErrors([podResult]) checkForErrors([podResult])
return JSON.parse(podResult.stdout).status return JSON.parse(podResult.stdout).status
@@ -151,10 +172,12 @@ function isPodReady(podStatus: any): boolean {
return allContainersAreReady return allContainersAreReady
} }
async function getService(kubectl: Kubectl, serviceName) { async function getService(kubectl: Kubectl, service: Resource) {
const serviceResult = await kubectl.getResource( const serviceResult = await kubectl.getResource(
KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE, KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE,
serviceName service.name,
IS_SILENT,
service.namespace
) )
checkForErrors([serviceResult]) checkForErrors([serviceResult])
@@ -163,25 +186,25 @@ async function getService(kubectl: Kubectl, serviceName) {
async function waitForServiceExternalIPAssignment( async function waitForServiceExternalIPAssignment(
kubectl: Kubectl, kubectl: Kubectl,
serviceName: string service: Resource
): 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 : ${serviceName}`) core.info(`Wait for service ip assignment : ${service.name}`)
await sleep(sleepTimeout) await sleep(sleepTimeout)
const status = (await getService(kubectl, serviceName)).status const status = (await getService(kubectl, service)).status
if (isLoadBalancerIPAssigned(status)) { if (isLoadBalancerIPAssigned(status)) {
core.info( core.info(
`ServiceExternalIP ${serviceName} ${status.loadBalancer.ingress[0].ip}` `ServiceExternalIP ${service.name} ${status.loadBalancer.ingress[0].ip}`
) )
return return
} }
} }
core.warning(`Wait for service ip assignment timed out${serviceName}`) core.warning(`Wait for service ip assignment timed out ${service.name}`)
} }
function isLoadBalancerIPAssigned(status: any) { function isLoadBalancerIPAssigned(status: any) {
+2 -1
View File
@@ -280,7 +280,8 @@ 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
}) })
} }
}) })