mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-06-21 18:59:27 +08:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef8692a58f | |||
| ca3f299adf | |||
| b75cce76a5 | |||
| 922d445a51 | |||
| 8d3326c0a3 |
@@ -1,36 +0,0 @@
|
||||
name: Bug Report
|
||||
description: File a bug report specifying all inputs you provided for the action, we will respond to this thread with any questions.
|
||||
title: 'Bug: '
|
||||
labels: ['bug', 'triage']
|
||||
assignees: '@Azure/aks-atlanta'
|
||||
body:
|
||||
- type: textarea
|
||||
id: What-happened
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: Tell us what happened and how is it different from the expected?
|
||||
placeholder: Tell us what you see!
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: Version
|
||||
attributes:
|
||||
label: Version
|
||||
options:
|
||||
- label: I am using the latest version
|
||||
required: true
|
||||
- type: input
|
||||
id: Runner
|
||||
attributes:
|
||||
label: Runner
|
||||
description: What runner are you using?
|
||||
placeholder: Mention the runner info (self-hosted, operating system)
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: Logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Run in debug mode for the most verbose logs. Please feel free to attach a screenshot of the logs
|
||||
validations:
|
||||
required: true
|
||||
@@ -1,6 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: GitHub Action "k8s-deploy" Support
|
||||
url: https://github.com/Azure/k8s-deploy
|
||||
security: https://github.com/Azure/k8s-deploy/blob/main/SECURITY.md
|
||||
about: Please ask and answer questions here.
|
||||
@@ -1,13 +0,0 @@
|
||||
name: Feature Request
|
||||
description: File a Feature Request form, we will respond to this thread with any questions.
|
||||
title: 'Feature Request: '
|
||||
labels: ['Feature']
|
||||
assignees: '@Azure/aks-atlanta'
|
||||
body:
|
||||
- type: textarea
|
||||
id: Feature_request
|
||||
attributes:
|
||||
label: Feature request
|
||||
description: Provide example functionality and links to relevant docs
|
||||
validations:
|
||||
required: true
|
||||
@@ -13,15 +13,20 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
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.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
@@ -29,7 +34,7 @@ jobs:
|
||||
# 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)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -43,4 +48,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
name: release Project
|
||||
name: Create release PR
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- CHANGELOG.md
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release:
|
||||
description: 'Define release version (ex: v1, v2, v3)'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: Azure/action-release-workflows/.github/workflows/release_js_project.yaml@81e6a8ed41ced9d131dea884ecae7b8c6dc4f799
|
||||
release-pr:
|
||||
uses: OliverMKing/javascript-release-workflow/.github/workflows/release-pr.yml@main
|
||||
with:
|
||||
changelogPath: ./CHANGELOG.md
|
||||
release: ${{ github.event.inputs.release }}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
name: Minikube Integration Tests - basic
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Install conntrack
|
||||
run: sudo apt-get install -y conntrack
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- uses: Azure/setup-kubectl@v3
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns ${{ env.NAMESPACE }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for pod
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments and services were created
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_basic selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_basic selectorLabels=app:nginx
|
||||
@@ -1,180 +0,0 @@
|
||||
name: Minikube Integration Tests - blue-green ingress
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Install conntrack
|
||||
run: sudo apt-get install -y conntrack
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- uses: Azure/setup-kubectl@v3
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.31.2
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns ${{ env.NAMESPACE }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service-green' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment-green' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'nginx-ingress' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments, services and ingresses were created with green labels and original tag
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-green containerName=nginx:1.14.2 labels=k8s.deploy.color:green,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-green labels=k8s.deploy.color:green,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Ingress name=nginx-ingress ingressServices=nginx-service-green,unrouted-service
|
||||
|
||||
- name: Executing promote action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: promote
|
||||
|
||||
- name: Checking if deployments, services and ingresses were created with none labels after first promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:None,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Ingress name=nginx-ingress ingressServices=nginx-service,unrouted-service
|
||||
|
||||
- name: Executing second deploy action for ingress with new tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:latest
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments (with new tag), services and ingresses were created with green labels after deploy, and old deployment persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-green containerName=nginx:latest labels=k8s.deploy.color:green,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-green labels=k8s.deploy.color:green,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:None,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Ingress name=nginx-ingress ingressServices=nginx-service-green,unrouted-service
|
||||
|
||||
- name: Executing second promote action for ingress now using new image tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:latest
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: promote
|
||||
|
||||
- name: Checking if deployments, services and ingresses were created with none labels after promote for new tag
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:None,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Ingress name=nginx-ingress ingressServices=nginx-service,unrouted-service
|
||||
|
||||
- name: Executing deploy action for ingress to be rejected using old tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: deploy
|
||||
|
||||
- name: Checking if new deployments (with old tag), services and ingresses were created with green labels after deploy, and old deployment (with latest tag) persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-green containerName=nginx:1.14.2 labels=k8s.deploy.color:green,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-green labels=k8s.deploy.color:green,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:None,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Ingress name=nginx-ingress ingressServices=nginx-service-green,unrouted-service
|
||||
|
||||
- name: Executing reject action for ingress to reject new deployment with 1.14.2 tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: reject
|
||||
|
||||
# MAY BE USEFUL TO ADD AN ANTI-CHECK - CHECK TO MAKE SURE CERTAIN OBJECTS DON'T EXIST
|
||||
- name: Checking if deployments, services and ingresses were created with none labels and latest tag after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:None,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_ingress selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Ingress name=nginx-ingress ingressServices=nginx-service,unrouted-service
|
||||
|
||||
- name: Cleaning up current set up
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ env.NAMESPACE }}
|
||||
|
||||
- if: ${{ always() }}
|
||||
name: Delete created namespace
|
||||
run: kubectl delete ns ${{ env.NAMESPACE }}
|
||||
@@ -1,167 +0,0 @@
|
||||
name: Minikube Integration Tests - blue-green service
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Install conntrack
|
||||
run: sudo apt-get install -y conntrack
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- uses: Azure/setup-kubectl@v3
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.31.2
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns ${{ env.NAMESPACE }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for service
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments and services were created with green labels and original tag
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-green containerName=nginx:1.14.2 labels=k8s.deploy.color:green,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:green,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
- name: Executing promote action for service
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: promote
|
||||
|
||||
- name: Checking if deployments and services were created with none labels after first promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:None,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
- name: Executing second deploy action for service with new tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:latest
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments (with new tag) and services were created with green labels after deploy, and old deployment persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-green containerName=nginx:latest labels=k8s.deploy.color:green,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:green,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
|
||||
- name: Executing second promote action for service now using new image tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:latest
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: promote
|
||||
|
||||
- name: Checking if deployments and services were created with none labels after promote for new tag
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:None,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
- name: Executing deploy action for service to be rejected using old tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: deploy
|
||||
|
||||
- name: Checking if new deployments (with old tag) and services were created with green labels after deploy, and old deployment (with latest tag) persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-green containerName=nginx:1.14.2 labels=k8s.deploy.color:green,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:green,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
|
||||
- name: Executing reject action for service to reject new deployment with 1.14.2 tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: reject
|
||||
|
||||
# MAY BE USEFUL TO ADD AN ANTI-CHECK - CHECK TO MAKE SURE CERTAIN OBJECTS DON'T EXIST
|
||||
- name: Checking if deployments and services were created with none labels and latest tag after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=k8s.deploy.color:None,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_service selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
- name: Cleaning up current set up
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ env.NAMESPACE }}
|
||||
|
||||
- if: ${{ always() }}
|
||||
name: Delete created namespace
|
||||
run: kubectl delete ns ${{ env.NAMESPACE }}
|
||||
@@ -1,205 +0,0 @@
|
||||
name: Minikube Integration Tests - blue-green SMI
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Install conntrack
|
||||
run: sudo apt-get install -y conntrack
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- uses: Azure/setup-kubectl@v3
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Install linkerd and add controlplane to cluster
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install | sh
|
||||
curl -sL https://linkerd.github.io/linkerd-smi/install | sh
|
||||
export PATH=$PATH:/home/runner/.linkerd2/bin
|
||||
|
||||
linkerd install --crds | kubectl apply -f -
|
||||
linkerd install --set proxyInit.runAsRoot=true | kubectl apply -f -
|
||||
linkerd smi install | kubectl apply -f -
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns ${{ env.NAMESPACE }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'TrafficSplit' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for smi
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: smi
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments, services, and ts objects were created with green labels and original tag
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-green containerName=nginx:1.14.2 labels=k8s.deploy.color:green,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI,k8s.deploy.color:None selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-green labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI,k8s.deploy.color:green selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-trafficsplit tsServices=nginx-service-stable:0,nginx-service-green:100
|
||||
|
||||
- name: Executing promote action for smi
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: smi
|
||||
action: promote
|
||||
|
||||
# another good place for anti-test - ensure old deps are deleted after promote
|
||||
- name: Checking if deployments, services, and ts objects were created with none labels after first promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI,k8s.deploy.color:None selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-trafficsplit tsServices=nginx-service-stable:100,nginx-service-green:0
|
||||
|
||||
- name: Executing second deploy action for smi with new tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:latest
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: smi
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments (with new tag) and services were created with green labels after deploy, and old deployment persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-green containerName=nginx:latest labels=k8s.deploy.color:green,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI,k8s.deploy.color:None selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-green labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI,k8s.deploy.color:green selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-trafficsplit tsServices=nginx-service-stable:0,nginx-service-green:100
|
||||
|
||||
- name: Executing second promote action for smi now using new image tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:latest
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: smi
|
||||
action: promote
|
||||
|
||||
- name: Checking if deployments and services were created with none labels after promote for new tag, ts is stable
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI,k8s.deploy.color:None selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-trafficsplit tsServices=nginx-service-stable:100,nginx-service-green:0
|
||||
|
||||
- name: Executing deploy action for smi to be rejected using old tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: smi
|
||||
action: deploy
|
||||
|
||||
- name: Checking if new deployments (with old tag) and services were created with green labels after deploy, and old deployment (with latest tag) persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-green containerName=nginx:1.14.2 labels=k8s.deploy.color:green,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI,k8s.deploy.color:None selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-green labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI,k8s.deploy.color:green selectorLabels=app:nginx,k8s.deploy.color:green
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-trafficsplit tsServices=nginx-service-stable:0,nginx-service-green:100
|
||||
|
||||
- name: Executing reject action for smi to reject new deployment with 1.14.2 tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/blue-green/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: smi
|
||||
action: reject
|
||||
|
||||
# MAY BE USEFUL TO ADD AN ANTI-CHECK - CHECK TO MAKE SURE CERTAIN OBJECTS DON'T EXIST
|
||||
- name: Checking if deployments and services were created with none labels and latest tag after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=k8s.deploy.color:None,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_blue-green_SMI,k8s.deploy.color:None selectorLabels=app:nginx,k8s.deploy.color:None
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-trafficsplit tsServices=nginx-service-stable:100,nginx-service-green:0
|
||||
|
||||
- name: Cleaning up current set up
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ env.NAMESPACE }}
|
||||
|
||||
- if: ${{ always() }}
|
||||
name: Delete created namespace
|
||||
run: kubectl delete ns ${{ env.NAMESPACE }}
|
||||
@@ -1,176 +0,0 @@
|
||||
name: Minikube Integration Tests - canary pod
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Install conntrack
|
||||
run: sudo apt-get install -y conntrack
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- uses: Azure/setup-kubectl@v3
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns ${{ env.NAMESPACE }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for pod
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: pod
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments and services were created with canary labels and original tag
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-canary containerName=nginx:1.14.2 labels=workflow/version:canary,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx,workflow/version:canary
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
|
||||
- name: Executing promote action for pod
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: pod
|
||||
action: promote
|
||||
|
||||
# another good place for anti-test - ensure old deps are deleted after promote
|
||||
- name: Checking if deployments and services were created with stable labels after first promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
|
||||
- name: Executing second deploy action for pod with new tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:latest
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: pod
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments (with new tag) and services were created with canary labels after deploy, and old deployment persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-canary containerName=nginx:latest labels=workflow/version:canary,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx,workflow/version:canary
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
|
||||
- name: Executing second promote action for pod now using new image tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:latest
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: pod
|
||||
action: promote
|
||||
|
||||
- name: Checking if deployments and services were created with stable labels after promote for new tag, ts is stable
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
|
||||
- name: Executing deploy action for pod to be rejected using old tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: pod
|
||||
action: deploy
|
||||
|
||||
- name: Checking if new deployments (with old tag) and services were created with canary and baseline labels after deploy, and stable deployment (with latest tag) persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-canary containerName=nginx:1.14.2 labels=workflow/version:canary,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx,workflow/version:canary
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
|
||||
- name: Executing reject action for pod to reject new deployment with 1.14.2 tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: pod
|
||||
action: reject
|
||||
|
||||
# MAY BE USEFUL TO ADD AN ANTI-CHECK - CHECK TO MAKE SURE CERTAIN OBJECTS DON'T EXIST
|
||||
- name: Checking if deployments and services were created with stable labels and latest tag after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:latest labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_pod selectorLabels=app:nginx
|
||||
|
||||
- name: Cleaning up current set up
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ env.NAMESPACE }}
|
||||
|
||||
- if: ${{ always() }}
|
||||
name: Delete created namespace
|
||||
run: kubectl delete ns ${{ env.NAMESPACE }}
|
||||
@@ -1,217 +0,0 @@
|
||||
name: Minikube Integration Tests - canary SMI
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Install conntrack
|
||||
run: sudo apt-get install -y conntrack
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- uses: Azure/setup-kubectl@v3
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Install linkerd and add controlplane to cluster
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install | sh
|
||||
curl -sL https://linkerd.github.io/linkerd-smi/install | sh
|
||||
export PATH=$PATH:/home/runner/.linkerd2/bin
|
||||
|
||||
linkerd install --crds | kubectl apply -f -
|
||||
linkerd install --set proxyInit.runAsRoot=true | kubectl apply -f -
|
||||
linkerd smi install | kubectl apply -f -
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns ${{ env.NAMESPACE }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'TrafficSplit' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for smi
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: smi
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments, services, and ts objects were created with canary labels and original tag
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-canary containerName=nginx:1.14.2 labels=workflow/version:canary,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx,workflow/version:canary
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-canary labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI,workflow/version:canary selectorLabels=app:nginx,workflow/version:canary
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-workflow-rollout tsServices=nginx-service-stable:0,nginx-service-canary:1000,nginx-service-baseline:0
|
||||
|
||||
- name: Executing promote action for smi
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: smi
|
||||
action: promote
|
||||
|
||||
# another good place for anti-test - ensure old deps are deleted after promote
|
||||
- name: Checking if deployments, services, and ts objects were created with stable labels after first promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-stable containerName=nginx:1.14.2 labels=workflow/version:stable,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx,workflow/version:stable
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI,workflow/version:stable selectorLabels=app:nginx,workflow/version:stable
|
||||
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-workflow-rollout tsServices=nginx-service-stable:1000,nginx-service-canary:0,nginx-service-baseline:0
|
||||
|
||||
- name: Executing second deploy action for smi with new tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:latest
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: smi
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments (with new tag) and services were created with canary labels after deploy, and old deployment persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-stable containerName=nginx:1.14.2 labels=workflow/version:stable,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx,workflow/version:stable
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-canary containerName=nginx:latest labels=workflow/version:canary,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx,workflow/version:canary
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-baseline containerName=nginx:1.14.2 labels=workflow/version:baseline,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx,workflow/version:baseline
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI,workflow/version:stable selectorLabels=app:nginx,workflow/version:stable
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-canary labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI,workflow/version:canary selectorLabels=app:nginx,workflow/version:canary
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-baseline labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI,workflow/version:baseline selectorLabels=app:nginx,workflow/version:baseline
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-workflow-rollout tsServices=nginx-service-stable:500,nginx-service-canary:250,nginx-service-baseline:250
|
||||
|
||||
- name: Executing second promote action for smi now using new image tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:latest
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: smi
|
||||
action: promote
|
||||
|
||||
- name: Checking if deployments and services were created with stable labels after promote for new tag, ts is stable
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-stable containerName=nginx:latest labels=workflow/version:stable,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx,workflow/version:stable
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI,workflow/version:stable selectorLabels=app:nginx,workflow/version:stable
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-workflow-rollout tsServices=nginx-service-stable:1000,nginx-service-canary:0,nginx-service-baseline:0
|
||||
|
||||
- name: Executing deploy action for smi to be rejected using old tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: smi
|
||||
action: deploy
|
||||
|
||||
- name: Checking if new deployments (with old tag) and services were created with canary and baseline labels after deploy, and stable deployment (with latest tag) persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-canary containerName=nginx:1.14.2 labels=workflow/version:canary,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx,workflow/version:canary
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-baseline containerName=nginx:latest labels=workflow/version:baseline,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx,workflow/version:baseline
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-stable containerName=nginx:latest labels=workflow/version:stable,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx,workflow/version:stable
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI,workflow/version:stable selectorLabels=app:nginx,workflow/version:stable
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-baseline labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI,workflow/version:baseline selectorLabels=app:nginx,workflow/version:baseline
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-canary labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI,workflow/version:canary selectorLabels=app:nginx,workflow/version:canary
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-workflow-rollout tsServices=nginx-service-stable:500,nginx-service-canary:250,nginx-service-baseline:250
|
||||
|
||||
- name: Executing reject action for smi to reject new deployment with 1.14.2 tag
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
strategy: canary
|
||||
percentage: 50
|
||||
traffic-split-method: smi
|
||||
action: reject
|
||||
|
||||
# MAY BE USEFUL TO ADD AN ANTI-CHECK - CHECK TO MAKE SURE CERTAIN OBJECTS DON'T EXIST
|
||||
- name: Checking if deployments and services were created with stable labels and latest tag after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment-stable containerName=nginx:latest labels=workflow/version:stable,app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx,workflow/version:stable
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service-stable labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_canary_SMI,workflow/version:stable selectorLabels=app:nginx,workflow/version:stable
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=TrafficSplit name=nginx-service-workflow-rollout tsServices=nginx-service-stable:1000,nginx-service-canary:0,nginx-service-baseline:0
|
||||
|
||||
- name: Cleaning up current set up
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'TrafficSplit' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- if: ${{ always() }}
|
||||
name: Delete created namespace
|
||||
run: kubectl delete ns ${{ env.NAMESPACE }}
|
||||
@@ -1,81 +0,0 @@
|
||||
name: Cluster Integration Tests - private cluster
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
- name: Azure login
|
||||
uses: azure/login@v1.4.3
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
|
||||
- uses: Azure/setup-kubectl@v3
|
||||
name: Install Kubectl
|
||||
|
||||
- name: Create private AKS cluster and set context
|
||||
run: |
|
||||
set +x
|
||||
# create cluster
|
||||
az group create --location eastus --name ${{ env.NAMESPACE }}
|
||||
az aks create --name ${{ env.NAMESPACE }} --resource-group ${{ env.NAMESPACE }} --enable-private-cluster --generate-ssh-keys
|
||||
az aks get-credentials --resource-group ${{ env.NAMESPACE }} --name ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: |
|
||||
az aks command invoke --resource-group ${{ env.NAMESPACE }} --name ${{ env.NAMESPACE }} --command "kubectl create ns ${{ env.NAMESPACE }}"
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Executing deploy action for pod
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
action: deploy
|
||||
private-cluster: true
|
||||
resource-group: ${{ env.NAMESPACE }}
|
||||
name: ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Checking if deployments and services were created
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py private=${{ env.NAMESPACE }} namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Cluster_Integration_Tests_-_private_cluster selectorLabels=app:nginx
|
||||
python test/integration/k8s-deploy-test.py private=${{ env.NAMESPACE }} namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Cluster_Integration_Tests_-_private_cluster selectorLabels=app:nginx
|
||||
|
||||
- name: Clean up AKS cluster
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
echo "deleting AKS cluster and resource group"
|
||||
az aks delete --yes --resource-group ${{ env.NAMESPACE }} --name ${{ env.NAMESPACE }}
|
||||
az group delete --resource-group ${{ env.NAMESPACE }} --yes
|
||||
@@ -1,89 +0,0 @@
|
||||
name: Minikube Integration Tests - resource annotation
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Install conntrack
|
||||
run: sudo apt-get install -y conntrack
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- uses: Azure/setup-kubectl@v3
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns ${{ env.NAMESPACE }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for pod with resource annotation enabled by default
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments is created with additional resource annotation
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_resource_annotation selectorLabels=app:nginx annotations=actions.github.com/k8s-deploy,deployment.kubernetes.io/revision,kubectl.kubernetes.io/last-applied-configuration
|
||||
|
||||
- name: Cleaning previously created deployment
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for pod with resource annotation disabled
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
action: deploy
|
||||
annotate-resources: false
|
||||
|
||||
- name: Checking if deployment is created without additional resource annotation
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 selectorLabels=app:nginx annotations=deployment.kubernetes.io/revision,kubectl.kubernetes.io/last-applied-configuration
|
||||
@@ -0,0 +1,215 @@
|
||||
name: Minikube Integration Tests
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- 'releases/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- name: Set name of ns
|
||||
run: echo "::set-output name=name::$(echo `date +%Y%m%d%H%M%S`)"
|
||||
shell: bash
|
||||
id: ns
|
||||
|
||||
- uses: Azure/setup-kubectl@v1
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: manusa/actions-setup-minikube@v2.4.2
|
||||
with:
|
||||
minikube version: 'v1.24.0'
|
||||
kubernetes version: 'v1.17.8'
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns test-${{ steps.ns.outputs.name }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service-green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment-green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'nginx-ingress' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing deploy action
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deploments and services were created with green labels
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'green' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing promote action
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: promote
|
||||
|
||||
- name: Checking if deploments and services were created with none labels after promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing deploy action on
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deploments and services were created with green labels, and old workloads persist on deploy
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing reject action
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: reject
|
||||
|
||||
- name: Checking if deploments and services were routed back to none labels after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Cleaning up current set up
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing deploy action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deploments, services and ingresses were created with green labels
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'green' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing promote action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: promote
|
||||
|
||||
- name: Checking if deploments, services and ingresses were created with none labels after promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'None' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing deploy action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deploments, services and ingresses were created with green labels after deploy, and old deployment persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'green' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing reject action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: reject
|
||||
|
||||
- name: Checking if deploments, services and ingresses were created with none labels after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'None' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- if: ${{ always() }}
|
||||
name: Delete created namespace
|
||||
run: kubectl delete ns test-${{ steps.ns.outputs.name }}
|
||||
|
||||
- if: ${{ always() }}
|
||||
name: Posting result back to PR
|
||||
run: |
|
||||
if [ '${{ steps.job-type.outputs.type }}' == 'pr' ]; then ruby postStatus.rb ${{github.event.client_payload.repository}} ${{github.event.client_payload.commit}} ${{secrets.L2_REPO_TOKEN}} ${{job.status}} ${{github.run_id}} ${{matrix.os}} false ${{ secrets.L2_REPO_USER }}; fi
|
||||
shell: bash
|
||||
@@ -11,10 +11,9 @@ on: # rebuild any PRs and main branch changes
|
||||
|
||||
jobs:
|
||||
build: # make sure build/ci works properly
|
||||
name: Run Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v1
|
||||
- run: |
|
||||
npm install
|
||||
npm test
|
||||
|
||||
@@ -2,5 +2,6 @@ node_modules
|
||||
|
||||
.DS_Store
|
||||
.idea
|
||||
lib/
|
||||
|
||||
coverage/
|
||||
@@ -1,11 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## [v4.10.0] - 2023-10-30
|
||||
|
||||
### Added
|
||||
|
||||
- #287 Make annotating resources optional
|
||||
- #283 Fix “Service” route-method of the Blue-Green strategy with some manifest files
|
||||
- #281 bump codeql to node 16
|
||||
- #279 upgrade codeql
|
||||
- #276 Fixes multiple namespaces bug
|
||||
@@ -51,7 +51,7 @@ Following are the key capabilities of this action:
|
||||
</tr>
|
||||
<tr>
|
||||
<td>manifests </br></br>(Required)</td>
|
||||
<td>Path to the manifest files to be used for deployment. These can also be directories containing manifest files, in which case, all manifest files in the referenced directory at every depth will be deployed, or URLs to manifest files (like https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/controllers/nginx-deployment.yaml). Files and URLs not ending in .yml or .yaml will be ignored.</td>
|
||||
<td>Path to the manifest files to be used for deployment. These can also be directories containing manifest files, in which case, all manifest files in the referenced directory at every depth will be deployed. Files not ending in .yml or .yaml will be ignored.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>strategy </br></br>(Required)</td>
|
||||
@@ -91,8 +91,8 @@ Following are the key capabilities of this action:
|
||||
<td>Used to compute the number of replicas of '-baseline' and '-canary' variants of the workloads found in manifest files. For the specified percentage input, if (percentage * numberOfDesirerdReplicas)/100 is not a round number, the floor of this number is used while creating '-baseline' and '-canary'.<br/><br/>For example, if Deployment hello-world was found in the input manifest file with 'replicas: 4' and if 'strategy: canary' and 'percentage: 25' are given as inputs to the action, then the Deployments hello-world-baseline and hello-world-canary are created with 1 replica each. The '-baseline' variant is created with the same image and tag as the stable version (4 replica variant prior to deployment) while the '-canary' variant is created with the image and tag corresponding to the new changes being deployed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>baseline-and-canary-replicas </br></br> (Optional and relevant only if strategy is canary and traffic-split-method is smi)</td>
|
||||
<td>The number of baseline and canary replicas. Percentage traffic split is controlled in the service mesh plane, the actual number of replicas for canary and baseline variants could be controlled independently of the traffic split. For example, assume that the input Deployment manifest desired 30 replicas to be used for stable and that the following inputs were specified for the action </br></br><code> strategy: canary<br> trafficSplitMethod: smi<br> percentage: 20<br> baselineAndCanaryReplicas: 1</code></br></br> In this case, stable variant will receive 80% traffic while baseline and canary variants will receive 10% each (20% split equally between baseline and canary). However, instead of creating baseline and canary with 3 replicas each, the explicit count of baseline and canary replicas is honored. That is, only 1 replica each is created for baseline and canary variants.</td>
|
||||
<td>baseline-and-canary-replicas </br></br> (Optional and relevant only if traffic-split-method is canary)</td>
|
||||
<td>The number of baseline and canary replicas. Percentage traffic split is controlled in the service mesh plane, the actual number of replicas for canary and baseline variants could be controlled independently of the traffic split. For example, assume that the input Deployment manifest desired 30 replicas to be used for stable and that the following inputs were specified for the action </br></br><code> strategy: canary<br> trafficSplitMethod: smi<br> percentage: 20<br> baselineAndCanaryReplicas: 1</code></br></br> In this case, stable variant will receive 80% traffic while baseline and canary variants will receive 10% each (20% split equally between baseline and canary). However, instead of creating baseline and canary with 3 replicas, the explicit count of baseline and canary replicas is honored. That is, only 1 replica each is created for baseline and canary variants.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>route-method </br></br>(Optional and relevant only if strategy is blue-green)</td>
|
||||
@@ -113,17 +113,9 @@ Following are the key capabilities of this action:
|
||||
<td>force </br></br>(Optional)</td>
|
||||
<td>Deploy when a previous deployment already exists. If true then '--force' argument is added to the apply command. Using '--force' argument is not recommended in production.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotate-resources</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the resources or not. If set to false all annotations are skipped completely.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotate-namespace</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the namespace resources object or not. Ignored when annotate-resources is set to false.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>skip-tls-verify</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: false</br>True if the insecure-skip-tls-verify option should be used</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the namespace resources object or not</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -132,7 +124,7 @@ Following are the key capabilities of this action:
|
||||
### Basic deployment (without any deployment strategy)
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
manifests: |
|
||||
@@ -166,7 +158,7 @@ Following are the key capabilities of this action:
|
||||
### Canary deployment without service mesh
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
@@ -185,7 +177,7 @@ Following are the key capabilities of this action:
|
||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
@@ -203,7 +195,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
### Canary deployment based on Service Mesh Interface
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
@@ -224,7 +216,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }} '
|
||||
@@ -243,7 +235,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
### Blue-Green deployment with different route methods
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
@@ -263,7 +255,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
To promote/reject the green workload created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
@@ -320,7 +312,7 @@ jobs:
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
action: deploy
|
||||
manifests: |
|
||||
@@ -366,7 +358,7 @@ jobs:
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
action: deploy
|
||||
manifests: |
|
||||
@@ -479,7 +471,3 @@ provided by the bot. You will only need to do this once across all repos using o
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Support
|
||||
|
||||
k8s-deploy is an open source project that is [**not** covered by the Microsoft Azure support policy](https://support.microsoft.com/en-us/help/2941892/support-for-linux-and-open-source-technology-in-azure). [Please search open issues here](https://github.com/Azure/k8s-deploy/issues), and if your issue isn't already represented please [open a new one](https://github.com/Azure/k8s-deploy/issues/new/choose). The project maintainers will respond to the best of their abilities.
|
||||
|
||||
+2
-10
@@ -6,7 +6,6 @@ inputs:
|
||||
namespace:
|
||||
description: 'Choose the target Kubernetes namespace. If the namespace is not provided, the commands will run in the default namespace.'
|
||||
required: false
|
||||
default: default
|
||||
manifests:
|
||||
description: 'Path to the manifest files which will be used for deployment.'
|
||||
required: true
|
||||
@@ -42,7 +41,7 @@ inputs:
|
||||
baseline-and-canary-replicas:
|
||||
description: 'Baseline and canary replicas count. Valid value between 0 to 100 (inclusive)'
|
||||
required: false
|
||||
default: ''
|
||||
default: 0
|
||||
percentage:
|
||||
description: 'Percentage of traffic redirect to canary deployment'
|
||||
required: false
|
||||
@@ -59,12 +58,8 @@ inputs:
|
||||
description: 'Github token'
|
||||
default: ${{ github.token }}
|
||||
required: true
|
||||
annotate-resources:
|
||||
description: 'Annotate the resources. If set to false all annotations are skipped completely.'
|
||||
required: false
|
||||
default: true
|
||||
annotate-namespace:
|
||||
description: 'Annotate the target namespace. Ignored when annotate-resources is set to false.'
|
||||
description: 'Annotate the target namespace'
|
||||
required: false
|
||||
default: true
|
||||
private-cluster:
|
||||
@@ -77,9 +72,6 @@ inputs:
|
||||
name:
|
||||
description: 'Resource group name - Only required if using private cluster'
|
||||
required: false
|
||||
skip-tls-verify:
|
||||
description: True if the insecure-skip-tls-verify option should be used. Input should be 'true' or 'false'.
|
||||
default: false
|
||||
|
||||
branding:
|
||||
color: 'green'
|
||||
|
||||
+1
-2
@@ -6,6 +6,5 @@ module.exports = {
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest'
|
||||
},
|
||||
verbose: true,
|
||||
testTimeout: 9000
|
||||
verbose: true
|
||||
}
|
||||
|
||||
-24128
File diff suppressed because one or more lines are too long
Generated
+234
-623
File diff suppressed because it is too large
Load Diff
+3
-5
@@ -4,28 +4,26 @@
|
||||
"author": "Deepak Sattiraju",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "npm i ncc && npx ncc build src/run.ts -o lib",
|
||||
"build": "ncc build src/run.ts -o lib",
|
||||
"test": "jest",
|
||||
"coverage": "jest --coverage=true",
|
||||
"format": "prettier --write .",
|
||||
"format-check": "prettier --check ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/core": "^1.2.6",
|
||||
"@actions/exec": "^1.0.0",
|
||||
"@actions/io": "^1.0.0",
|
||||
"@actions/tool-cache": "1.1.2",
|
||||
"@octokit/core": "^3.5.1",
|
||||
"@octokit/plugin-retry": "^3.0.9",
|
||||
"@types/minipass": "^3.1.2",
|
||||
"js-yaml": "3.13.1",
|
||||
"ncc": "^0.3.6"
|
||||
"js-yaml": "3.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.0",
|
||||
"@types/js-yaml": "^3.12.7",
|
||||
"@types/node": "^12.20.41",
|
||||
"@vercel/ncc": "^0.36.1",
|
||||
"jest": "^26.0.0",
|
||||
"prettier": "^2.7.1",
|
||||
"ts-jest": "^26.0.0",
|
||||
|
||||
@@ -56,19 +56,24 @@ export async function deploy(
|
||||
for (const ingressResource of ingressResources) {
|
||||
await kubectl.getResource(
|
||||
KubernetesConstants.DiscoveryAndLoadBalancerResource.INGRESS,
|
||||
ingressResource.name,
|
||||
false,
|
||||
ingressResource.namespace
|
||||
ingressResource.name
|
||||
)
|
||||
}
|
||||
core.endGroup()
|
||||
|
||||
// annotate resources
|
||||
core.startGroup('Annotating resources')
|
||||
let allPods
|
||||
try {
|
||||
allPods = JSON.parse((await kubectl.getAllPods()).stdout)
|
||||
} catch (e) {
|
||||
core.debug(`Unable to parse pods: ${e}`)
|
||||
}
|
||||
await annotateAndLabelResources(
|
||||
deployedManifestFiles,
|
||||
kubectl,
|
||||
resourceTypes
|
||||
resourceTypes,
|
||||
allPods
|
||||
)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
+8
-49
@@ -1,12 +1,11 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as deploy from './deploy'
|
||||
import * as canaryDeploymentHelper from '../strategyHelpers/canary/canaryHelper'
|
||||
import * as SMICanaryDeploymentHelper from '../strategyHelpers/canary/smiCanaryHelper'
|
||||
import * as PodCanaryHelper from '../strategyHelpers/canary/podCanaryHelper'
|
||||
import {
|
||||
getResources,
|
||||
updateManifestFiles
|
||||
} from '../utilities/manifestUpdateUtils'
|
||||
import {annotateAndLabelResources} from '../strategyHelpers/deploymentHelper'
|
||||
import * as models from '../types/kubernetesTypes'
|
||||
import * as KubernetesManifestUtility from '../utilities/manifestStabilityUtils'
|
||||
import {
|
||||
@@ -16,7 +15,6 @@ import {
|
||||
} from '../strategyHelpers/blueGreen/blueGreenHelper'
|
||||
|
||||
import {BlueGreenManifests} from '../types/blueGreenTypes'
|
||||
import {DeployResult} from '../types/deployResult'
|
||||
|
||||
import {
|
||||
promoteBlueGreenIngress,
|
||||
@@ -59,13 +57,9 @@ export async function promote(
|
||||
async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
|
||||
let includeServices = false
|
||||
|
||||
const manifestFilesForDeployment: string[] = updateManifestFiles(manifests)
|
||||
|
||||
const trafficSplitMethod = parseTrafficSplitMethod(
|
||||
core.getInput('traffic-split-method', {required: true})
|
||||
)
|
||||
let promoteResult: DeployResult
|
||||
let filesToAnnotate: string[]
|
||||
if (trafficSplitMethod == TrafficSplitMethod.SMI) {
|
||||
includeServices = true
|
||||
|
||||
@@ -78,38 +72,19 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
|
||||
)
|
||||
core.endGroup()
|
||||
|
||||
core.startGroup(
|
||||
'Deploying input manifests with SMI canary strategy from promote'
|
||||
)
|
||||
|
||||
promoteResult = await SMICanaryDeploymentHelper.deploySMICanary(
|
||||
manifestFilesForDeployment,
|
||||
kubectl,
|
||||
true
|
||||
)
|
||||
|
||||
core.startGroup('Deploying input manifests with SMI canary strategy')
|
||||
await deploy.deploy(kubectl, manifests, DeploymentStrategy.CANARY)
|
||||
core.endGroup()
|
||||
|
||||
core.startGroup('Redirecting traffic to stable deployment')
|
||||
const stableRedirectManifests =
|
||||
await SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(
|
||||
kubectl,
|
||||
manifests
|
||||
)
|
||||
|
||||
filesToAnnotate = promoteResult.manifestFiles.concat(
|
||||
stableRedirectManifests
|
||||
await SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(
|
||||
kubectl,
|
||||
manifests
|
||||
)
|
||||
|
||||
core.endGroup()
|
||||
} else {
|
||||
core.startGroup('Deploying input manifests from promote')
|
||||
promoteResult = await PodCanaryHelper.deployPodCanary(
|
||||
manifestFilesForDeployment,
|
||||
kubectl,
|
||||
true
|
||||
)
|
||||
filesToAnnotate = promoteResult.manifestFiles
|
||||
core.startGroup('Deploying input manifests')
|
||||
await deploy.deploy(kubectl, manifests, DeploymentStrategy.CANARY)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
@@ -126,17 +101,6 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
|
||||
)
|
||||
}
|
||||
core.endGroup()
|
||||
|
||||
// annotate resources
|
||||
core.startGroup('Annotating resources')
|
||||
const resources: Resource[] = getResources(
|
||||
filesToAnnotate,
|
||||
models.DEPLOYMENT_TYPES.concat([
|
||||
models.DiscoveryAndLoadBalancerResource.SERVICE
|
||||
])
|
||||
)
|
||||
await annotateAndLabelResources(filesToAnnotate, kubectl, resources)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
async function promoteBlueGreen(kubectl: Kubectl, manifests: string[]) {
|
||||
@@ -210,9 +174,4 @@ async function promoteBlueGreen(kubectl: Kubectl, manifests: string[]) {
|
||||
await deleteGreenObjects(kubectl, manifestObjects.deploymentEntityList)
|
||||
}
|
||||
core.endGroup()
|
||||
|
||||
// annotate resources
|
||||
core.startGroup('Annotating resources')
|
||||
await annotateAndLabelResources(deployedManifestFiles, kubectl, resources)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
+4
-7
@@ -5,7 +5,7 @@ import {promote} from './actions/promote'
|
||||
import {reject} from './actions/reject'
|
||||
import {Action, parseAction} from './types/action'
|
||||
import {parseDeploymentStrategy} from './types/deploymentStrategy'
|
||||
import {getFilesFromDirectoriesAndURLs} from './utilities/fileUtils'
|
||||
import {getFilesFromDirectories} from './utilities/fileUtils'
|
||||
import {PrivateKubectl} from './types/privatekubectl'
|
||||
|
||||
export async function run() {
|
||||
@@ -26,26 +26,23 @@ export async function run() {
|
||||
.map((manifest) => manifest.trim()) // remove surrounding whitespace
|
||||
.filter((manifest) => manifest.length > 0) // remove any blanks
|
||||
|
||||
const fullManifestFilePaths = await getFilesFromDirectoriesAndURLs(
|
||||
manifestFilePaths
|
||||
)
|
||||
const fullManifestFilePaths = getFilesFromDirectories(manifestFilePaths)
|
||||
const kubectlPath = await getKubectlPath()
|
||||
const namespace = core.getInput('namespace') || 'default'
|
||||
const isPrivateCluster =
|
||||
core.getInput('private-cluster').toLowerCase() === 'true'
|
||||
const resourceGroup = core.getInput('resource-group') || ''
|
||||
const resourceName = core.getInput('name') || ''
|
||||
const skipTlsVerify = core.getBooleanInput('skip-tls-verify')
|
||||
|
||||
const kubectl = isPrivateCluster
|
||||
? new PrivateKubectl(
|
||||
kubectlPath,
|
||||
namespace,
|
||||
skipTlsVerify,
|
||||
true,
|
||||
resourceGroup,
|
||||
resourceName
|
||||
)
|
||||
: new Kubectl(kubectlPath, namespace, skipTlsVerify)
|
||||
: new Kubectl(kubectlPath, namespace, true)
|
||||
|
||||
// run action
|
||||
switch (action) {
|
||||
|
||||
@@ -38,8 +38,7 @@ export async function deleteGreenObjects(
|
||||
const resourcesToDelete: K8sDeleteObject[] = toDelete.map((obj) => {
|
||||
return {
|
||||
name: getBlueGreenResourceName(obj.metadata.name, GREEN_SUFFIX),
|
||||
kind: obj.kind,
|
||||
namespace: obj.metadata.namespace
|
||||
kind: obj.kind
|
||||
}
|
||||
})
|
||||
|
||||
@@ -67,25 +66,31 @@ export async function deleteObjects(
|
||||
// other common functions
|
||||
export function getManifestObjects(filePaths: string[]): BlueGreenManifests {
|
||||
const deploymentEntityList: K8sObject[] = []
|
||||
const serviceEntityList: K8sObject[] = []
|
||||
const routedServiceEntityList: K8sObject[] = []
|
||||
const unroutedServiceEntityList: K8sObject[] = []
|
||||
const ingressEntityList: K8sObject[] = []
|
||||
const otherEntitiesList: K8sObject[] = []
|
||||
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) => {
|
||||
const fileContents = fs.readFileSync(filePath).toString()
|
||||
yaml.safeLoadAll(fileContents, (inputObject) => {
|
||||
if (!!inputObject) {
|
||||
const kind = inputObject.kind
|
||||
const name = inputObject.metadata.name
|
||||
|
||||
if (isDeploymentEntity(kind)) {
|
||||
deploymentEntityList.push(inputObject)
|
||||
} else if (isServiceEntity(kind)) {
|
||||
serviceEntityList.push(inputObject)
|
||||
if (isServiceRouted(inputObject, deploymentEntityList)) {
|
||||
routedServiceEntityList.push(inputObject)
|
||||
serviceNameMap.set(
|
||||
name,
|
||||
getBlueGreenResourceName(name, GREEN_SUFFIX)
|
||||
)
|
||||
} else {
|
||||
unroutedServiceEntityList.push(inputObject)
|
||||
}
|
||||
} else if (isIngressEntity(kind)) {
|
||||
ingressEntityList.push(inputObject)
|
||||
} else {
|
||||
@@ -95,16 +100,6 @@ export function getManifestObjects(filePaths: string[]): BlueGreenManifests {
|
||||
})
|
||||
})
|
||||
|
||||
serviceEntityList.forEach((inputObject: any) => {
|
||||
if (isServiceRouted(inputObject, deploymentEntityList)) {
|
||||
const name = inputObject.metadata.name
|
||||
routedServiceEntityList.push(inputObject)
|
||||
serviceNameMap.set(name, getBlueGreenResourceName(name, GREEN_SUFFIX))
|
||||
} else {
|
||||
unroutedServiceEntityList.push(inputObject)
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
serviceEntityList: routedServiceEntityList,
|
||||
serviceNameMap: serviceNameMap,
|
||||
@@ -239,10 +234,9 @@ export function isServiceSelectorSubsetOfMatchLabel(
|
||||
export async function fetchResource(
|
||||
kubectl: Kubectl,
|
||||
kind: string,
|
||||
name: string,
|
||||
namespace?: string
|
||||
name: string
|
||||
): Promise<K8sObject> {
|
||||
const result = await kubectl.getResource(kind, name, false, namespace)
|
||||
const result = await kubectl.getResource(kind, name)
|
||||
if (result == null || !!result.stderr) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ describe('deploy tests', () => {
|
||||
RouteStrategy.SMI
|
||||
)
|
||||
|
||||
expect(smiResult.objects.length).toBe(6)
|
||||
expect(smiResult.objects.length).toBe(3)
|
||||
})
|
||||
|
||||
test('correctly deploys blue/green ingress', async () => {
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
import {setupSMI} from './smiBlueGreenHelper'
|
||||
|
||||
import {routeBlueGreenForDeploy} from './route'
|
||||
import {DeployResult} from '../../types/deployResult'
|
||||
|
||||
export async function deployBlueGreen(
|
||||
kubectl: Kubectl,
|
||||
@@ -36,17 +35,9 @@ export async function deployBlueGreen(
|
||||
})()
|
||||
|
||||
core.startGroup('Routing blue green')
|
||||
const routeDeployment = await routeBlueGreenForDeploy(
|
||||
kubectl,
|
||||
files,
|
||||
routeStrategy
|
||||
)
|
||||
await routeBlueGreenForDeploy(kubectl, files, routeStrategy)
|
||||
core.endGroup()
|
||||
|
||||
blueGreenDeployment.objects.push(...routeDeployment.objects)
|
||||
blueGreenDeployment.deployResult.manifestFiles.push(
|
||||
...routeDeployment.deployResult.manifestFiles
|
||||
)
|
||||
return blueGreenDeployment
|
||||
}
|
||||
|
||||
@@ -65,16 +56,10 @@ export async function deployBlueGreenSMI(
|
||||
manifestObjects.unroutedServiceEntityList
|
||||
)
|
||||
|
||||
const otherObjDeployment: DeployResult = await deployObjects(
|
||||
kubectl,
|
||||
newObjectsList
|
||||
)
|
||||
await deployObjects(kubectl, newObjectsList)
|
||||
|
||||
// make extraservices and trafficsplit
|
||||
const smiAndSvcDeployment = await setupSMI(
|
||||
kubectl,
|
||||
manifestObjects.serviceEntityList
|
||||
)
|
||||
await setupSMI(kubectl, manifestObjects.serviceEntityList)
|
||||
|
||||
// create new deloyments
|
||||
const blueGreenDeployment: BlueGreenDeployment = await deployWithLabel(
|
||||
@@ -82,18 +67,10 @@ export async function deployBlueGreenSMI(
|
||||
manifestObjects.deploymentEntityList,
|
||||
GREEN_LABEL_VALUE
|
||||
)
|
||||
|
||||
blueGreenDeployment.objects.push(...newObjectsList)
|
||||
blueGreenDeployment.objects.push(...smiAndSvcDeployment.objects)
|
||||
|
||||
blueGreenDeployment.deployResult.manifestFiles.push(
|
||||
...otherObjDeployment.manifestFiles
|
||||
)
|
||||
blueGreenDeployment.deployResult.manifestFiles.push(
|
||||
...smiAndSvcDeployment.deployResult.manifestFiles
|
||||
)
|
||||
|
||||
return blueGreenDeployment
|
||||
return {
|
||||
deployResult: blueGreenDeployment.deployResult,
|
||||
objects: [].concat(blueGreenDeployment.objects, newObjectsList)
|
||||
}
|
||||
}
|
||||
|
||||
export async function deployBlueGreenIngress(
|
||||
|
||||
@@ -97,8 +97,7 @@ export async function validateIngresses(
|
||||
const existingIngress = await fetchResource(
|
||||
kubectl,
|
||||
inputObject.kind,
|
||||
inputObject.metadata.name,
|
||||
inputObject?.metadata?.namespace
|
||||
inputObject.metadata.name
|
||||
)
|
||||
|
||||
const isValid =
|
||||
|
||||
@@ -61,6 +61,6 @@ describe('reject tests', () => {
|
||||
.spyOn(TSutils, 'getTrafficSplitAPIVersion')
|
||||
.mockImplementation(() => Promise.resolve('v1alpha3'))
|
||||
const rejectResult = await rejectBlueGreenSMI(kubectl, testObjects)
|
||||
expect(rejectResult.deleteResult).toHaveLength(2)
|
||||
expect(rejectResult.deleteResult).toHaveLength(4)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -31,8 +31,7 @@ export async function validateServicesState(
|
||||
const existingService = await fetchResource(
|
||||
kubectl,
|
||||
serviceObject.kind,
|
||||
serviceObject.metadata.name,
|
||||
serviceObject?.metadata?.namespace
|
||||
serviceObject.metadata.name
|
||||
)
|
||||
|
||||
let isServiceGreen =
|
||||
|
||||
@@ -193,8 +193,11 @@ describe('SMI Helper tests', () => {
|
||||
|
||||
test('cleanupSMI test', async () => {
|
||||
const deleteObjects = await cleanupSMI(kc, testObjects.serviceEntityList)
|
||||
expect(deleteObjects).toHaveLength(1)
|
||||
expect(deleteObjects[0].name).toBe('nginx-service-green')
|
||||
expect(deleteObjects[0].kind).toBe('Service')
|
||||
expect(deleteObjects).toHaveLength(3)
|
||||
expect(deleteObjects[0].name).toBe('nginx-service-trafficsplit')
|
||||
expect(deleteObjects[1].name).toBe('nginx-service-green')
|
||||
expect(deleteObjects[1].kind).toBe('Service')
|
||||
expect(deleteObjects[2].name).toBe('nginx-service-stable')
|
||||
expect(deleteObjects[2].kind).toBe('Service')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -142,8 +142,7 @@ export async function validateTrafficSplitsState(
|
||||
let trafficSplitObject = await fetchResource(
|
||||
kubectl,
|
||||
TRAFFIC_SPLIT_OBJECT,
|
||||
getBlueGreenResourceName(name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX),
|
||||
serviceObject?.metadata?.namespace
|
||||
getBlueGreenResourceName(name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX)
|
||||
)
|
||||
core.debug(
|
||||
`ts object extracted was ${JSON.stringify(trafficSplitObject)}`
|
||||
@@ -179,13 +178,28 @@ export async function cleanupSMI(
|
||||
const deleteList: K8sDeleteObject[] = []
|
||||
|
||||
serviceEntityList.forEach((serviceObject) => {
|
||||
deleteList.push({
|
||||
name: getBlueGreenResourceName(
|
||||
serviceObject.metadata.name,
|
||||
TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX
|
||||
),
|
||||
kind: TRAFFIC_SPLIT_OBJECT
|
||||
})
|
||||
|
||||
deleteList.push({
|
||||
name: getBlueGreenResourceName(
|
||||
serviceObject.metadata.name,
|
||||
GREEN_SUFFIX
|
||||
),
|
||||
kind: serviceObject.kind,
|
||||
namespace: serviceObject?.metadata?.namespace
|
||||
kind: serviceObject.kind
|
||||
})
|
||||
|
||||
deleteList.push({
|
||||
name: getBlueGreenResourceName(
|
||||
serviceObject.metadata.name,
|
||||
STABLE_SUFFIX
|
||||
),
|
||||
kind: serviceObject.kind
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import {Kubectl} from '../../types/kubectl'
|
||||
import * as fs from 'fs'
|
||||
import * as yaml from 'js-yaml'
|
||||
import * as core from '@actions/core'
|
||||
import {ExecOutput} from '@actions/exec'
|
||||
import {
|
||||
isDeploymentEntity,
|
||||
isServiceEntity,
|
||||
@@ -29,17 +28,12 @@ export async function deleteCanaryDeployment(
|
||||
kubectl: Kubectl,
|
||||
manifestFilePaths: string[],
|
||||
includeServices: boolean
|
||||
): Promise<string[]> {
|
||||
) {
|
||||
if (manifestFilePaths == null || manifestFilePaths.length == 0) {
|
||||
throw new Error('Manifest files for deleting canary deployment not found')
|
||||
throw new Error('Manifest file not found')
|
||||
}
|
||||
|
||||
const deletedFiles = await cleanUpCanary(
|
||||
kubectl,
|
||||
manifestFilePaths,
|
||||
includeServices
|
||||
)
|
||||
return deletedFiles
|
||||
await cleanUpCanary(kubectl, manifestFilePaths, includeServices)
|
||||
}
|
||||
|
||||
export function markResourceAsStable(inputObject: any): object {
|
||||
@@ -60,7 +54,7 @@ export function isResourceMarkedAsStable(inputObject: any): boolean {
|
||||
|
||||
export function getStableResource(inputObject: any): object {
|
||||
const replicaCount = specContainsReplicas(inputObject.kind)
|
||||
? inputObject.spec.replicas
|
||||
? inputObject.metadata.replicas
|
||||
: 0
|
||||
|
||||
return getNewCanaryObject(inputObject, replicaCount, STABLE_LABEL_VALUE)
|
||||
@@ -85,12 +79,7 @@ export async function fetchResource(
|
||||
kind: string,
|
||||
name: string
|
||||
) {
|
||||
let result: ExecOutput
|
||||
try {
|
||||
result = await kubectl.getResource(kind, name)
|
||||
} catch (e) {
|
||||
core.debug(`detected error while fetching resources: ${e}`)
|
||||
}
|
||||
const result = await kubectl.getResource(kind, name)
|
||||
|
||||
if (!result || result?.stderr) {
|
||||
return null
|
||||
@@ -104,7 +93,7 @@ export async function fetchResource(
|
||||
return resource
|
||||
} catch (ex) {
|
||||
core.debug(
|
||||
`Exception occurred while parsing ${resource} in JSON object: ${ex}`
|
||||
`Exception occurred while Parsing ${resource} in JSON object: ${ex}`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -122,26 +111,6 @@ export function getStableResourceName(name: string) {
|
||||
return name + STABLE_SUFFIX
|
||||
}
|
||||
|
||||
export function getBaselineDeploymentFromStableDeployment(
|
||||
inputObject: any,
|
||||
replicaCount: number
|
||||
): object {
|
||||
// TODO: REFACTOR TO MAKE EVERYTHING TYPE SAFE
|
||||
const oldName = inputObject.metadata.name
|
||||
const newName =
|
||||
oldName.substring(0, oldName.length - STABLE_SUFFIX.length) +
|
||||
BASELINE_SUFFIX
|
||||
|
||||
const newObject = getNewCanaryObject(
|
||||
inputObject,
|
||||
replicaCount,
|
||||
BASELINE_LABEL_VALUE
|
||||
) as any
|
||||
newObject.metadata.name = newName
|
||||
|
||||
return newObject
|
||||
}
|
||||
|
||||
function getNewCanaryObject(
|
||||
inputObject: any,
|
||||
replicas: number,
|
||||
@@ -194,22 +163,16 @@ async function cleanUpCanary(
|
||||
kubectl: Kubectl,
|
||||
files: string[],
|
||||
includeServices: boolean
|
||||
): Promise<string[]> {
|
||||
const deleteObject = async function (
|
||||
kind: string,
|
||||
name: string,
|
||||
namespace: string | undefined
|
||||
) {
|
||||
) {
|
||||
const deleteObject = async function (kind, name) {
|
||||
try {
|
||||
const result = await kubectl.delete([kind, name], namespace)
|
||||
const result = await kubectl.delete([kind, name])
|
||||
checkForErrors([result])
|
||||
} catch (ex) {
|
||||
// Ignore failures of delete if it doesn't exist
|
||||
}
|
||||
}
|
||||
|
||||
const deletedFiles: string[] = []
|
||||
|
||||
for (const filePath of files) {
|
||||
const fileContents = fs.readFileSync(filePath).toString()
|
||||
|
||||
@@ -217,21 +180,17 @@ async function cleanUpCanary(
|
||||
for (const inputObject of parsedYaml) {
|
||||
const name = inputObject.metadata.name
|
||||
const kind = inputObject.kind
|
||||
const namespace: string | undefined = inputObject?.metadata?.namespace
|
||||
|
||||
if (
|
||||
isDeploymentEntity(kind) ||
|
||||
(includeServices && isServiceEntity(kind))
|
||||
) {
|
||||
deletedFiles.push(filePath)
|
||||
const canaryObjectName = getCanaryResourceName(name)
|
||||
const baselineObjectName = getBaselineResourceName(name)
|
||||
|
||||
await deleteObject(kind, canaryObjectName, namespace)
|
||||
await deleteObject(kind, baselineObjectName, namespace)
|
||||
await deleteObject(kind, canaryObjectName)
|
||||
await deleteObject(kind, baselineObjectName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deletedFiles
|
||||
}
|
||||
|
||||
@@ -7,15 +7,10 @@ import * as fileHelper from '../../utilities/fileUtils'
|
||||
import * as canaryDeploymentHelper from './canaryHelper'
|
||||
import {isDeploymentEntity} from '../../types/kubernetesTypes'
|
||||
import {getReplicaCount} from '../../utilities/manifestUpdateUtils'
|
||||
import {DeployResult} from '../../types/deployResult'
|
||||
|
||||
export async function deployPodCanary(
|
||||
filePaths: string[],
|
||||
kubectl: Kubectl,
|
||||
onlyDeployStable: boolean = false
|
||||
): Promise<DeployResult> {
|
||||
export async function deployPodCanary(filePaths: string[], kubectl: Kubectl) {
|
||||
const newObjectsList = []
|
||||
const percentage = parseInt(core.getInput('percentage', {required: true}))
|
||||
const percentage = parseInt(core.getInput('percentage'))
|
||||
|
||||
if (percentage < 0 || percentage > 100)
|
||||
throw Error('Percentage must be between 0 and 100')
|
||||
@@ -27,7 +22,7 @@ export async function deployPodCanary(
|
||||
const name = inputObject.metadata.name
|
||||
const kind = inputObject.kind
|
||||
|
||||
if (!onlyDeployStable && isDeploymentEntity(kind)) {
|
||||
if (isDeploymentEntity(kind)) {
|
||||
core.debug('Calculating replica count for canary')
|
||||
const canaryReplicaCount = calculateReplicaCountForCanary(
|
||||
inputObject,
|
||||
@@ -35,22 +30,37 @@ export async function deployPodCanary(
|
||||
)
|
||||
core.debug('Replica count is ' + canaryReplicaCount)
|
||||
|
||||
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(
|
||||
inputObject,
|
||||
canaryReplicaCount
|
||||
)
|
||||
newObjectsList.push(newCanaryObject)
|
||||
|
||||
// if there's already a stable object, deploy baseline as well
|
||||
// Get stable object
|
||||
core.debug('Querying stable object')
|
||||
const stableObject = await canaryDeploymentHelper.fetchResource(
|
||||
kubectl,
|
||||
kind,
|
||||
name
|
||||
)
|
||||
if (stableObject) {
|
||||
|
||||
if (!stableObject) {
|
||||
core.debug('Stable object not found. Creating canary object')
|
||||
const newCanaryObject =
|
||||
canaryDeploymentHelper.getNewCanaryResource(
|
||||
inputObject,
|
||||
canaryReplicaCount
|
||||
)
|
||||
newObjectsList.push(newCanaryObject)
|
||||
} else {
|
||||
core.debug(
|
||||
`Stable object found for ${kind} ${name}. Creating baseline objects`
|
||||
'Creating canary and baseline objects. Stable object found: ' +
|
||||
JSON.stringify(stableObject)
|
||||
)
|
||||
|
||||
const newCanaryObject =
|
||||
canaryDeploymentHelper.getNewCanaryResource(
|
||||
inputObject,
|
||||
canaryReplicaCount
|
||||
)
|
||||
core.debug(
|
||||
'New canary object: ' + JSON.stringify(newCanaryObject)
|
||||
)
|
||||
|
||||
const newBaselineObject =
|
||||
canaryDeploymentHelper.getNewBaselineResource(
|
||||
stableObject,
|
||||
@@ -59,10 +69,12 @@ export async function deployPodCanary(
|
||||
core.debug(
|
||||
'New baseline object: ' + JSON.stringify(newBaselineObject)
|
||||
)
|
||||
|
||||
newObjectsList.push(newCanaryObject)
|
||||
newObjectsList.push(newBaselineObject)
|
||||
}
|
||||
} else {
|
||||
// deploy non deployment entity or regular deployments for promote as they are
|
||||
// update non deployment entity as it is
|
||||
newObjectsList.push(inputObject)
|
||||
}
|
||||
}
|
||||
@@ -72,14 +84,11 @@ export async function deployPodCanary(
|
||||
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
||||
const forceDeployment = core.getInput('force').toLowerCase() === 'true'
|
||||
|
||||
const execResult = await kubectl.apply(manifestFiles, forceDeployment)
|
||||
return {execResult, manifestFiles}
|
||||
const result = await kubectl.apply(manifestFiles, forceDeployment)
|
||||
return {result, newFilePaths: manifestFiles}
|
||||
}
|
||||
|
||||
export function calculateReplicaCountForCanary(
|
||||
inputObject: any,
|
||||
percentage: number
|
||||
) {
|
||||
function calculateReplicaCountForCanary(inputObject: any, percentage: number) {
|
||||
const inputReplicaCount = getReplicaCount(inputObject)
|
||||
return Math.max(1, Math.round((inputReplicaCount * percentage) / 100))
|
||||
return Math.round((inputReplicaCount * percentage) / 100)
|
||||
}
|
||||
|
||||
@@ -6,109 +6,84 @@ import * as yaml from 'js-yaml'
|
||||
import * as fileHelper from '../../utilities/fileUtils'
|
||||
import * as kubectlUtils from '../../utilities/trafficSplitUtils'
|
||||
import * as canaryDeploymentHelper from './canaryHelper'
|
||||
import * as podCanaryHelper from './podCanaryHelper'
|
||||
import {isDeploymentEntity, isServiceEntity} from '../../types/kubernetesTypes'
|
||||
import {checkForErrors} from '../../utilities/kubectlUtils'
|
||||
import {inputAnnotations} from '../../inputUtils'
|
||||
import {DeployResult} from '../../types/deployResult'
|
||||
|
||||
const TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX = '-workflow-rollout'
|
||||
const TRAFFIC_SPLIT_OBJECT = 'TrafficSplit'
|
||||
|
||||
export async function deploySMICanary(
|
||||
filePaths: string[],
|
||||
kubectl: Kubectl,
|
||||
onlyDeployStable: boolean = false
|
||||
): Promise<DeployResult> {
|
||||
const canaryReplicasInput = core.getInput('baseline-and-canary-replicas')
|
||||
let canaryReplicaCount
|
||||
let calculateReplicas = true
|
||||
if (canaryReplicasInput !== '') {
|
||||
canaryReplicaCount = parseInt(canaryReplicasInput)
|
||||
calculateReplicas = false
|
||||
core.debug(
|
||||
`read replica count ${canaryReplicaCount} from input: ${canaryReplicasInput}`
|
||||
)
|
||||
}
|
||||
|
||||
if (canaryReplicaCount < 0 && canaryReplicaCount > 100)
|
||||
export async function deploySMICanary(filePaths: string[], kubectl: Kubectl) {
|
||||
const canaryReplicaCount = parseInt(
|
||||
core.getInput('baseline-and-canary-replicas')
|
||||
)
|
||||
if (canaryReplicaCount < 0 || canaryReplicaCount > 100)
|
||||
throw Error('Baseline-and-canary-replicas must be between 0 and 100')
|
||||
|
||||
const newObjectsList = []
|
||||
for await (const filePath of filePaths) {
|
||||
filePaths.forEach((filePath: string) => {
|
||||
const fileContents = fs.readFileSync(filePath).toString()
|
||||
const inputObjects = yaml.safeLoadAll(fileContents)
|
||||
for (const inputObject of inputObjects) {
|
||||
yaml.safeLoadAll(fileContents, (inputObject) => {
|
||||
const name = inputObject.metadata.name
|
||||
const kind = inputObject.kind
|
||||
|
||||
if (!onlyDeployStable && isDeploymentEntity(kind)) {
|
||||
if (calculateReplicas) {
|
||||
// calculate for each object
|
||||
const percentage = parseInt(
|
||||
core.getInput('percentage', {required: true})
|
||||
)
|
||||
canaryReplicaCount =
|
||||
podCanaryHelper.calculateReplicaCountForCanary(
|
||||
inputObject,
|
||||
percentage
|
||||
)
|
||||
core.debug(`calculated replica count ${canaryReplicaCount}`)
|
||||
}
|
||||
|
||||
core.debug('Creating canary object')
|
||||
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(
|
||||
inputObject,
|
||||
canaryReplicaCount
|
||||
)
|
||||
newObjectsList.push(newCanaryObject)
|
||||
|
||||
const stableObject = await canaryDeploymentHelper.fetchResource(
|
||||
if (isDeploymentEntity(kind)) {
|
||||
const stableObject = canaryDeploymentHelper.fetchResource(
|
||||
kubectl,
|
||||
kind,
|
||||
canaryDeploymentHelper.getStableResourceName(name)
|
||||
name
|
||||
)
|
||||
if (stableObject) {
|
||||
|
||||
if (!stableObject) {
|
||||
core.debug(
|
||||
`Stable object found for ${kind} ${name}. Creating baseline objects`
|
||||
'Stable object not found. Creating only canary object'
|
||||
)
|
||||
const newCanaryObject =
|
||||
canaryDeploymentHelper.getNewCanaryResource(
|
||||
inputObject,
|
||||
canaryReplicaCount
|
||||
)
|
||||
newObjectsList.push(newCanaryObject)
|
||||
} else {
|
||||
if (
|
||||
!canaryDeploymentHelper.isResourceMarkedAsStable(stableObject)
|
||||
) {
|
||||
throw Error(`StableSpecSelectorNotExist : ${name}`)
|
||||
}
|
||||
|
||||
core.debug(
|
||||
'Stable object found. Creating canary and baseline objects'
|
||||
)
|
||||
const newCanaryObject =
|
||||
canaryDeploymentHelper.getNewCanaryResource(
|
||||
inputObject,
|
||||
canaryReplicaCount
|
||||
)
|
||||
const newBaselineObject =
|
||||
canaryDeploymentHelper.getBaselineDeploymentFromStableDeployment(
|
||||
canaryDeploymentHelper.getNewBaselineResource(
|
||||
stableObject,
|
||||
canaryReplicaCount
|
||||
)
|
||||
newObjectsList.push(newCanaryObject)
|
||||
newObjectsList.push(newBaselineObject)
|
||||
}
|
||||
} else if (isDeploymentEntity(kind)) {
|
||||
core.debug(
|
||||
`creating stable deployment with ${inputObject.spec.replicas} replicas`
|
||||
)
|
||||
const stableDeployment =
|
||||
canaryDeploymentHelper.getStableResource(inputObject)
|
||||
newObjectsList.push(stableDeployment)
|
||||
} else {
|
||||
// Update non deployment entity or stable deployment as it is
|
||||
// Update non deployment entity as it is
|
||||
newObjectsList.push(inputObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
core.debug(
|
||||
`deploying canary objects with SMI: \n ${JSON.stringify(newObjectsList)}`
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const newFilePaths = fileHelper.writeObjectsToFile(newObjectsList)
|
||||
const forceDeployment = core.getInput('force').toLowerCase() === 'true'
|
||||
const result = await kubectl.apply(newFilePaths, forceDeployment)
|
||||
const svcDeploymentFiles = await createCanaryService(kubectl, filePaths)
|
||||
newFilePaths.push(...svcDeploymentFiles)
|
||||
return {execResult: result, manifestFiles: newFilePaths}
|
||||
await createCanaryService(kubectl, filePaths)
|
||||
return {result, newFilePaths}
|
||||
}
|
||||
|
||||
async function createCanaryService(
|
||||
kubectl: Kubectl,
|
||||
filePaths: string[]
|
||||
): Promise<string[]> {
|
||||
async function createCanaryService(kubectl: Kubectl, filePaths: string[]) {
|
||||
const newObjectsList = []
|
||||
const trafficObjectsList: string[] = []
|
||||
const trafficObjectsList = []
|
||||
|
||||
for (const filePath of filePaths) {
|
||||
const fileContents = fs.readFileSync(filePath).toString()
|
||||
@@ -118,7 +93,6 @@ async function createCanaryService(
|
||||
const kind = inputObject.kind
|
||||
|
||||
if (isServiceEntity(kind)) {
|
||||
core.debug(`Creating services for ${kind} ${name}`)
|
||||
const newCanaryServiceObject =
|
||||
canaryDeploymentHelper.getNewCanaryResource(inputObject)
|
||||
newObjectsList.push(newCanaryServiceObject)
|
||||
@@ -181,7 +155,7 @@ async function createCanaryService(
|
||||
name
|
||||
)
|
||||
trafficObjectsList.push(
|
||||
await updateTrafficSplitObject(kubectl, name)
|
||||
updateTrafficSplitObject(kubectl, name)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -195,7 +169,6 @@ async function createCanaryService(
|
||||
|
||||
const result = await kubectl.apply(manifestFiles, forceDeployment)
|
||||
checkForErrors([result])
|
||||
return manifestFiles
|
||||
}
|
||||
|
||||
export async function redirectTrafficToCanaryDeployment(
|
||||
@@ -208,8 +181,8 @@ export async function redirectTrafficToCanaryDeployment(
|
||||
export async function redirectTrafficToStableDeployment(
|
||||
kubectl: Kubectl,
|
||||
manifestFilePaths: string[]
|
||||
): Promise<string[]> {
|
||||
return await adjustTraffic(kubectl, manifestFilePaths, 1000, 0)
|
||||
) {
|
||||
await adjustTraffic(kubectl, manifestFilePaths, 1000, 0)
|
||||
}
|
||||
|
||||
async function adjustTraffic(
|
||||
@@ -251,14 +224,13 @@ async function adjustTraffic(
|
||||
const forceDeployment = core.getInput('force').toLowerCase() === 'true'
|
||||
const result = await kubectl.apply(trafficSplitManifests, forceDeployment)
|
||||
checkForErrors([result])
|
||||
return trafficSplitManifests
|
||||
}
|
||||
|
||||
async function updateTrafficSplitObject(
|
||||
kubectl: Kubectl,
|
||||
serviceName: string
|
||||
): Promise<string> {
|
||||
const percentage = parseInt(core.getInput('percentage', {required: true}))
|
||||
const percentage = parseInt(core.getInput('percentage'))
|
||||
if (percentage < 0 || percentage > 100)
|
||||
throw Error('Percentage must be between 0 and 100')
|
||||
|
||||
@@ -269,9 +241,9 @@ async function updateTrafficSplitObject(
|
||||
core.debug(
|
||||
'Creating the traffic object with canary weight: ' +
|
||||
baselineAndCanaryWeight +
|
||||
', baseline weight: ' +
|
||||
',baseling weight: ' +
|
||||
baselineAndCanaryWeight +
|
||||
', stable weight: ' +
|
||||
',stable: ' +
|
||||
stableDeploymentWeight
|
||||
)
|
||||
return await createTrafficSplitManifestFile(
|
||||
|
||||
@@ -39,8 +39,6 @@ import {
|
||||
normalizeWorkflowStrLabel
|
||||
} from '../utilities/githubUtils'
|
||||
import {getDeploymentConfig} from '../utilities/dockerUtils'
|
||||
import {deploy} from '../actions/deploy'
|
||||
import {DeployResult} from '../types/deployResult'
|
||||
|
||||
export async function deployManifests(
|
||||
files: string[],
|
||||
@@ -50,13 +48,13 @@ export async function deployManifests(
|
||||
): Promise<string[]> {
|
||||
switch (deploymentStrategy) {
|
||||
case DeploymentStrategy.CANARY: {
|
||||
const canaryDeployResult: DeployResult =
|
||||
const {result, newFilePaths} =
|
||||
trafficSplitMethod == TrafficSplitMethod.SMI
|
||||
? await deploySMICanary(files, kubectl)
|
||||
: await deployPodCanary(files, kubectl)
|
||||
|
||||
checkForErrors([canaryDeployResult.execResult])
|
||||
return canaryDeployResult.manifestFiles
|
||||
checkForErrors([result])
|
||||
return newFilePaths
|
||||
}
|
||||
|
||||
case DeploymentStrategy.BLUE_GREEN: {
|
||||
@@ -75,12 +73,7 @@ export async function deployManifests(
|
||||
)
|
||||
|
||||
checkForErrors([blueGreenDeployment.deployResult.execResult])
|
||||
const deployedManifestFiles =
|
||||
blueGreenDeployment.deployResult.manifestFiles
|
||||
core.debug(
|
||||
`from blue-green service, deployed manifest files are ${deployedManifestFiles}`
|
||||
)
|
||||
return deployedManifestFiles
|
||||
return blueGreenDeployment.deployResult.manifestFiles
|
||||
}
|
||||
|
||||
case DeploymentStrategy.BASIC: {
|
||||
@@ -147,45 +140,32 @@ export async function checkManifestStability(
|
||||
export async function annotateAndLabelResources(
|
||||
files: string[],
|
||||
kubectl: Kubectl,
|
||||
resourceTypes: Resource[]
|
||||
resourceTypes: Resource[],
|
||||
allPods: any
|
||||
) {
|
||||
const defaultWorkflowFileName = 'k8s-deploy-failed-workflow-annotation'
|
||||
const githubToken = core.getInput('token')
|
||||
let workflowFilePath
|
||||
try {
|
||||
workflowFilePath = await getWorkflowFilePath(githubToken)
|
||||
} catch (ex) {
|
||||
core.warning(`Failed to extract workflow file name: ${ex}`)
|
||||
workflowFilePath = defaultWorkflowFileName
|
||||
}
|
||||
const workflowFilePath = await getWorkflowFilePath(githubToken)
|
||||
|
||||
const deploymentConfig = await getDeploymentConfig()
|
||||
const annotationKeyLabel = getWorkflowAnnotationKeyLabel()
|
||||
|
||||
const shouldAnnotateResources = !(
|
||||
core.getInput('annotate-resources').toLowerCase() === 'false'
|
||||
)
|
||||
|
||||
if (shouldAnnotateResources) {
|
||||
await annotateResources(
|
||||
files,
|
||||
kubectl,
|
||||
resourceTypes,
|
||||
annotationKeyLabel,
|
||||
workflowFilePath,
|
||||
deploymentConfig
|
||||
).catch((err) => core.warning(`Failed to annotate resources: ${err} `))
|
||||
}
|
||||
|
||||
await labelResources(files, kubectl, annotationKeyLabel).catch((err) =>
|
||||
core.warning(`Failed to label resources: ${err}`)
|
||||
await annotateResources(
|
||||
files,
|
||||
kubectl,
|
||||
resourceTypes,
|
||||
allPods,
|
||||
annotationKeyLabel,
|
||||
workflowFilePath,
|
||||
deploymentConfig
|
||||
)
|
||||
await labelResources(files, kubectl, annotationKeyLabel)
|
||||
}
|
||||
|
||||
async function annotateResources(
|
||||
files: string[],
|
||||
kubectl: Kubectl,
|
||||
resourceTypes: Resource[],
|
||||
allPods: any,
|
||||
annotationKey: string,
|
||||
workflowFilePath: string,
|
||||
deploymentConfig: DeploymentConfig
|
||||
@@ -198,18 +178,6 @@ async function annotateResources(
|
||||
annotationKey
|
||||
)
|
||||
|
||||
if (core.isDebug()) {
|
||||
core.debug(`files getting annotated are ${JSON.stringify(files)}`)
|
||||
for (const filePath of files) {
|
||||
core.debug('printing objects getting annotated...')
|
||||
const fileContents = fs.readFileSync(filePath).toString()
|
||||
const inputObjects = yaml.safeLoadAll(fileContents)
|
||||
for (const inputObject of inputObjects) {
|
||||
core.debug(`object: ${JSON.stringify(inputObject)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const annotationKeyValStr = `${annotationKey}=${getWorkflowAnnotations(
|
||||
lastSuccessSha,
|
||||
workflowFilePath,
|
||||
@@ -221,27 +189,10 @@ async function annotateResources(
|
||||
)
|
||||
if (annotateNamespace) {
|
||||
annotateResults.push(
|
||||
await kubectl.annotate(
|
||||
'namespace',
|
||||
namespace,
|
||||
annotationKeyValStr,
|
||||
namespace
|
||||
)
|
||||
await kubectl.annotate('namespace', namespace, annotationKeyValStr)
|
||||
)
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const annotateResult = await kubectl.annotateFiles(
|
||||
file,
|
||||
annotationKeyValStr,
|
||||
namespace
|
||||
)
|
||||
annotateResults.push(annotateResult)
|
||||
} catch (e) {
|
||||
core.warning(`failed to annotate resource: ${e}`)
|
||||
}
|
||||
}
|
||||
annotateResults.push(await kubectl.annotateFiles(files, annotationKeyValStr))
|
||||
|
||||
for (const resource of resourceTypes) {
|
||||
if (
|
||||
@@ -253,8 +204,8 @@ async function annotateResources(
|
||||
kubectl,
|
||||
resource.type,
|
||||
resource.name,
|
||||
resource.namespace,
|
||||
annotationKeyValStr
|
||||
annotationKeyValStr,
|
||||
allPods
|
||||
)
|
||||
).forEach((execResult) => annotateResults.push(execResult))
|
||||
}
|
||||
@@ -275,14 +226,5 @@ async function labelResources(
|
||||
`workflow=${cleanLabel(label)}`
|
||||
]
|
||||
|
||||
const labelResults = []
|
||||
for (const file of files) {
|
||||
try {
|
||||
const labelResult = await kubectl.labelFiles(file, labels)
|
||||
labelResults.push(labelResult)
|
||||
} catch (e) {
|
||||
core.warning(`failed to annotate resource: ${e}`)
|
||||
}
|
||||
}
|
||||
checkForErrors(labelResults, true)
|
||||
checkForErrors([await kubectl.labelFiles(files, labels)], true)
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
export interface Succeeded<T> {
|
||||
readonly succeeded: true
|
||||
readonly result: T
|
||||
}
|
||||
|
||||
export interface Failed {
|
||||
readonly succeeded: false
|
||||
readonly error: string
|
||||
}
|
||||
|
||||
export type Errorable<T> = Succeeded<T> | Failed
|
||||
|
||||
export function succeeded<T>(e: Errorable<T>): e is Succeeded<T> {
|
||||
return e.succeeded
|
||||
}
|
||||
|
||||
export function failed<T>(e: Errorable<T>): e is Failed {
|
||||
return !e.succeeded
|
||||
}
|
||||
|
||||
export function map<T, U>(e: Errorable<T>, fn: (t: T) => U): Errorable<U> {
|
||||
if (failed(e)) {
|
||||
return {succeeded: false, error: e.error}
|
||||
}
|
||||
return {succeeded: true, result: fn(e.result)}
|
||||
}
|
||||
|
||||
export function combine<T>(es: Errorable<T>[]): Errorable<T[]> {
|
||||
const failures = es.filter(failed)
|
||||
if (failures.length > 0) {
|
||||
return {
|
||||
succeeded: false,
|
||||
error: failures.map((f) => f.error).join('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
succeeded: true,
|
||||
result: es.map((e) => (e as Succeeded<T>).result)
|
||||
}
|
||||
}
|
||||
|
||||
export function getErrorMessage(error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
return error.message
|
||||
}
|
||||
return String(error)
|
||||
}
|
||||
@@ -2,7 +2,6 @@ export interface K8sObject {
|
||||
metadata: {
|
||||
name: string
|
||||
labels: Map<string, string>
|
||||
namespace?: string
|
||||
}
|
||||
kind: string
|
||||
spec: any
|
||||
@@ -17,7 +16,6 @@ export interface K8sServiceObject extends K8sObject {
|
||||
export interface K8sDeleteObject {
|
||||
name: string
|
||||
kind: string
|
||||
namespace?: string
|
||||
}
|
||||
|
||||
export interface K8sIngress extends K8sObject {
|
||||
|
||||
+12
-193
@@ -3,6 +3,7 @@ import * as exec from '@actions/exec'
|
||||
import * as io from '@actions/io'
|
||||
import * as core from '@actions/core'
|
||||
import * as toolCache from '@actions/tool-cache'
|
||||
import {config} from 'process'
|
||||
|
||||
describe('Kubectl path', () => {
|
||||
const version = '1.1'
|
||||
@@ -37,7 +38,6 @@ describe('Kubectl path', () => {
|
||||
const kubectlPath = 'kubectlPath'
|
||||
const testNamespace = 'testNamespace'
|
||||
const defaultNamespace = 'default'
|
||||
const otherNamespace = 'otherns'
|
||||
describe('Kubectl class', () => {
|
||||
describe('default namespace behavior', () => {
|
||||
const kubectl = new Kubectl(kubectlPath, defaultNamespace)
|
||||
@@ -48,6 +48,17 @@ describe('Kubectl class', () => {
|
||||
return execReturn
|
||||
})
|
||||
})
|
||||
|
||||
describe('omits default namespace from commands', () => {
|
||||
it('executes a command without appending --namespace arg', async () => {
|
||||
// no args
|
||||
const command = 'command'
|
||||
expect(await kubectl.executeCommand(command)).toBe(execReturn)
|
||||
expect(exec.getExecOutput).toBeCalledWith(kubectlPath, [command], {
|
||||
silent: false
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a success exec return in testNamespace', () => {
|
||||
@@ -122,26 +133,6 @@ describe('Kubectl class', () => {
|
||||
],
|
||||
{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 () => {
|
||||
@@ -160,26 +151,6 @@ describe('Kubectl class', () => {
|
||||
],
|
||||
{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 () => {
|
||||
@@ -205,27 +176,6 @@ describe('Kubectl class', () => {
|
||||
],
|
||||
{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 () => {
|
||||
@@ -246,22 +196,6 @@ describe('Kubectl class', () => {
|
||||
],
|
||||
{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 () => {
|
||||
@@ -282,22 +216,6 @@ describe('Kubectl class', () => {
|
||||
],
|
||||
{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 () => {
|
||||
@@ -318,21 +236,6 @@ describe('Kubectl class', () => {
|
||||
],
|
||||
{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 () => {
|
||||
@@ -353,21 +256,6 @@ describe('Kubectl class', () => {
|
||||
],
|
||||
{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 () => {
|
||||
@@ -396,20 +284,6 @@ describe('Kubectl class', () => {
|
||||
],
|
||||
{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 () => {
|
||||
@@ -428,22 +302,6 @@ describe('Kubectl class', () => {
|
||||
],
|
||||
{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 () => {
|
||||
@@ -474,14 +332,6 @@ describe('Kubectl class', () => {
|
||||
['delete', arg, '--namespace', testNamespace],
|
||||
{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 () => {
|
||||
@@ -492,14 +342,6 @@ describe('Kubectl class', () => {
|
||||
['delete', ...args, '--namespace', testNamespace],
|
||||
{silent: false}
|
||||
)
|
||||
|
||||
// override ns
|
||||
await kubectl.delete(args, otherNamespace)
|
||||
expect(exec.getExecOutput).toBeCalledWith(
|
||||
kubectlPath,
|
||||
['delete', ...args, '--namespace', otherNamespace],
|
||||
{silent: false}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -522,27 +364,4 @@ describe('Kubectl class', () => {
|
||||
const result = await kubectl.getNewReplicaSet(deployment)
|
||||
expect(result).toBe(name)
|
||||
})
|
||||
|
||||
it('executes with constructor flags', async () => {
|
||||
const skipTls = true
|
||||
const kubectl = new Kubectl(kubectlPath, testNamespace, skipTls)
|
||||
|
||||
jest.spyOn(exec, 'getExecOutput').mockImplementation(async () => {
|
||||
return {exitCode: 0, stderr: '', stdout: ''}
|
||||
})
|
||||
|
||||
const command = 'command'
|
||||
kubectl.executeCommand(command)
|
||||
expect(exec.getExecOutput).toBeCalledWith(
|
||||
kubectlPath,
|
||||
[command, '--insecure-skip-tls-verify', '--namespace', testNamespace],
|
||||
{silent: false}
|
||||
)
|
||||
|
||||
const kubectlNoFlags = new Kubectl(kubectlPath)
|
||||
kubectlNoFlags.executeCommand(command)
|
||||
expect(exec.getExecOutput).toBeCalledWith(kubectlPath, [command], {
|
||||
silent: false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+37
-81
@@ -7,7 +7,6 @@ import * as io from '@actions/io'
|
||||
export interface Resource {
|
||||
name: string
|
||||
type: string
|
||||
namespace?: string
|
||||
}
|
||||
|
||||
export class Kubectl {
|
||||
@@ -20,7 +19,7 @@ export class Kubectl {
|
||||
|
||||
constructor(
|
||||
kubectlPath: string,
|
||||
namespace: string = '',
|
||||
namespace: string = 'default',
|
||||
ignoreSSLErrors: boolean = false,
|
||||
resourceGroup: string = '',
|
||||
name: string = ''
|
||||
@@ -47,7 +46,7 @@ export class Kubectl {
|
||||
]
|
||||
if (force) applyArgs.push('--force')
|
||||
|
||||
return await this.execute(applyArgs.concat(this.getFlags()))
|
||||
return await this.execute(applyArgs)
|
||||
} catch (err) {
|
||||
core.debug('Kubectl apply failed:' + err)
|
||||
}
|
||||
@@ -56,43 +55,27 @@ export class Kubectl {
|
||||
public async describe(
|
||||
resourceType: string,
|
||||
resourceName: string,
|
||||
silent: boolean = false,
|
||||
namespace?: string
|
||||
silent: boolean = false
|
||||
): Promise<ExecOutput> {
|
||||
return await this.execute(
|
||||
['describe', resourceType, resourceName].concat(
|
||||
this.getFlags(namespace)
|
||||
),
|
||||
['describe', resourceType, resourceName],
|
||||
silent
|
||||
)
|
||||
}
|
||||
|
||||
public async getNewReplicaSet(deployment: string, namespace?: string) {
|
||||
const result = await this.describe(
|
||||
'deployment',
|
||||
deployment,
|
||||
true,
|
||||
namespace
|
||||
)
|
||||
public async getNewReplicaSet(deployment: string) {
|
||||
const result = await this.describe('deployment', deployment, true)
|
||||
|
||||
let newReplicaSet = ''
|
||||
if (result?.stdout) {
|
||||
const stdout = result.stdout.split('\n')
|
||||
core.debug('stdout from getNewReplicaSet is ' + JSON.stringify(stdout))
|
||||
stdout.forEach((line: string) => {
|
||||
const newreplicaset = 'newreplicaset'
|
||||
if (line && line.toLowerCase().indexOf(newreplicaset) > -1) {
|
||||
core.debug(
|
||||
`found string of interest for replicaset, line is ${line}`
|
||||
)
|
||||
core.debug(
|
||||
`substring is ${line.substring(newreplicaset.length).trim()}`
|
||||
)
|
||||
if (line && line.toLowerCase().indexOf(newreplicaset) > -1)
|
||||
newReplicaSet = line
|
||||
.substring(newreplicaset.length)
|
||||
.trim()
|
||||
.split(' ')[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -102,8 +85,7 @@ export class Kubectl {
|
||||
public async annotate(
|
||||
resourceType: string,
|
||||
resourceName: string,
|
||||
annotation: string,
|
||||
namespace?: string
|
||||
annotation: string
|
||||
): Promise<ExecOutput> {
|
||||
const args = [
|
||||
'annotate',
|
||||
@@ -111,31 +93,27 @@ export class Kubectl {
|
||||
resourceName,
|
||||
annotation,
|
||||
'--overwrite'
|
||||
].concat(this.getFlags(namespace))
|
||||
]
|
||||
return await this.execute(args)
|
||||
}
|
||||
|
||||
public async annotateFiles(
|
||||
files: string | string[],
|
||||
annotation: string,
|
||||
namespace?: string
|
||||
annotation: string
|
||||
): Promise<ExecOutput> {
|
||||
const filesToAnnotate = createInlineArray(files)
|
||||
core.debug(`annotating ${filesToAnnotate} with annotation ${annotation}`)
|
||||
const args = [
|
||||
'annotate',
|
||||
'-f',
|
||||
filesToAnnotate,
|
||||
createInlineArray(files),
|
||||
annotation,
|
||||
'--overwrite'
|
||||
].concat(this.getFlags(namespace))
|
||||
]
|
||||
return await this.execute(args)
|
||||
}
|
||||
|
||||
public async labelFiles(
|
||||
files: string | string[],
|
||||
labels: string[],
|
||||
namespace?: string
|
||||
labels: string[]
|
||||
): Promise<ExecOutput> {
|
||||
const args = [
|
||||
'label',
|
||||
@@ -143,78 +121,56 @@ export class Kubectl {
|
||||
createInlineArray(files),
|
||||
...labels,
|
||||
'--overwrite'
|
||||
].concat(this.getFlags(namespace))
|
||||
]
|
||||
return await this.execute(args)
|
||||
}
|
||||
|
||||
public async getAllPods(): Promise<ExecOutput> {
|
||||
return await this.execute(
|
||||
['get', 'pods', '-o', 'json'].concat(this.getFlags()),
|
||||
true
|
||||
)
|
||||
return await this.execute(['get', 'pods', '-o', 'json'], true)
|
||||
}
|
||||
|
||||
public async checkRolloutStatus(
|
||||
resourceType: string,
|
||||
name: string,
|
||||
namespace?: string
|
||||
name: string
|
||||
): Promise<ExecOutput> {
|
||||
return await this.execute(
|
||||
['rollout', 'status', `${resourceType}/${name}`].concat(
|
||||
this.getFlags(namespace)
|
||||
)
|
||||
)
|
||||
return await this.execute([
|
||||
'rollout',
|
||||
'status',
|
||||
`${resourceType}/${name}`
|
||||
])
|
||||
}
|
||||
|
||||
public async getResource(
|
||||
resourceType: string,
|
||||
name: string,
|
||||
silentFailure: boolean = false,
|
||||
namespace?: string
|
||||
name: string
|
||||
): Promise<ExecOutput> {
|
||||
core.debug(
|
||||
'fetching resource of type ' + resourceType + ' and name ' + name
|
||||
)
|
||||
return await this.execute(
|
||||
['get', `${resourceType}/${name}`, '-o', 'json'].concat(
|
||||
this.getFlags(namespace)
|
||||
),
|
||||
silentFailure
|
||||
)
|
||||
return await this.execute([
|
||||
'get',
|
||||
`${resourceType}/${name}`,
|
||||
'-o',
|
||||
'json'
|
||||
])
|
||||
}
|
||||
|
||||
public executeCommand(command: string, args?: string) {
|
||||
if (!command) throw new Error('Command must be defined')
|
||||
const a = args ? [args] : []
|
||||
return this.execute([command, ...a.concat(this.getFlags())])
|
||||
return args ? this.execute([command, args]) : this.execute([command])
|
||||
}
|
||||
|
||||
public delete(args: string | string[], namespace?: string) {
|
||||
if (typeof args === 'string')
|
||||
return this.execute(['delete', args].concat(this.getFlags(namespace)))
|
||||
return this.execute(['delete', ...args.concat(this.getFlags(namespace))])
|
||||
public delete(args: string | string[]) {
|
||||
if (typeof args === 'string') return this.execute(['delete', args])
|
||||
return this.execute(['delete', ...args])
|
||||
}
|
||||
|
||||
protected async execute(args: string[], silent: boolean = false) {
|
||||
core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`)
|
||||
|
||||
return await getExecOutput(this.kubectlPath, args, {
|
||||
silent
|
||||
})
|
||||
}
|
||||
|
||||
protected getFlags(namespaceOverride?: string): string[] {
|
||||
const flags = []
|
||||
if (this.ignoreSSLErrors) {
|
||||
flags.push('--insecure-skip-tls-verify')
|
||||
args.push('--insecure-skip-tls-verify')
|
||||
}
|
||||
|
||||
const ns = namespaceOverride || this.namespace
|
||||
if (ns) {
|
||||
flags.push('--namespace', ns)
|
||||
if (this.namespace && this.namespace != 'default') {
|
||||
args = args.concat(['--namespace', this.namespace])
|
||||
}
|
||||
|
||||
return flags
|
||||
core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`)
|
||||
return await getExecOutput(this.kubectlPath, args, {silent})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import {PrivateKubectl} from './privatekubectl'
|
||||
import * as exec from '@actions/exec'
|
||||
|
||||
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 mockKube = new PrivateKubectl(
|
||||
'kubectlPath',
|
||||
'namespace',
|
||||
true,
|
||||
'resourceGroup',
|
||||
'resourceName'
|
||||
)
|
||||
|
||||
it('should extract filenames correctly', () => {
|
||||
expect(mockKube.extractFilesnames(testString)).toEqual(
|
||||
'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}`
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
+15
-63
@@ -1,5 +1,4 @@
|
||||
import {Kubectl} from './kubectl'
|
||||
import * as minimist from 'minimist'
|
||||
import {ExecOptions, ExecOutput, getExecOutput} from '@actions/exec'
|
||||
import * as core from '@actions/core'
|
||||
import * as os from 'os'
|
||||
@@ -11,11 +10,7 @@ export class PrivateKubectl extends Kubectl {
|
||||
args.unshift('kubectl')
|
||||
let kubectlCmd = args.join(' ')
|
||||
let addFileFlag = false
|
||||
let eo = <ExecOptions>{
|
||||
silent: true,
|
||||
failOnStdErr: false,
|
||||
ignoreReturnCode: true
|
||||
}
|
||||
let eo = <ExecOptions>{silent}
|
||||
|
||||
if (this.containsFilenames(kubectlCmd)) {
|
||||
// For private clusters, files will referenced solely by their basename
|
||||
@@ -23,13 +18,6 @@ export class PrivateKubectl extends Kubectl {
|
||||
addFileFlag = true
|
||||
}
|
||||
|
||||
if (this.resourceGroup === '') {
|
||||
throw Error('Resource group must be specified for private cluster')
|
||||
}
|
||||
if (this.name === '') {
|
||||
throw Error('Cluster name must be specified for private cluster')
|
||||
}
|
||||
|
||||
const privateClusterArgs = [
|
||||
'aks',
|
||||
'command',
|
||||
@@ -39,7 +27,7 @@ export class PrivateKubectl extends Kubectl {
|
||||
'--name',
|
||||
this.name,
|
||||
'--command',
|
||||
`${kubectlCmd}`
|
||||
kubectlCmd
|
||||
]
|
||||
|
||||
if (addFileFlag) {
|
||||
@@ -64,35 +52,7 @@ export class PrivateKubectl extends Kubectl {
|
||||
core.debug(
|
||||
`private cluster Kubectl run with invoke command: ${kubectlCmd}`
|
||||
)
|
||||
|
||||
const allArgs = [...privateClusterArgs, '-o', 'json']
|
||||
core.debug(`full form of az command: az ${allArgs.join(' ')}`)
|
||||
const runOutput = await getExecOutput('az', allArgs, eo)
|
||||
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: {logs: string; exitCode: number} = JSON.parse(
|
||||
runOutput.stdout
|
||||
)
|
||||
if (!silent) core.info(runObj.logs)
|
||||
if (runObj.exitCode !== 0) {
|
||||
throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`)
|
||||
}
|
||||
|
||||
return {
|
||||
exitCode: runObj.exitCode,
|
||||
stdout: runObj.logs,
|
||||
stderr: ''
|
||||
} as ExecOutput
|
||||
return await getExecOutput('az', privateClusterArgs, eo)
|
||||
}
|
||||
|
||||
private replaceFilnamesWithBasenames(kubectlCmd: string) {
|
||||
@@ -111,31 +71,23 @@ export class PrivateKubectl extends Kubectl {
|
||||
}
|
||||
|
||||
public extractFilesnames(strToParse: string) {
|
||||
const fileNames: string[] = []
|
||||
const argv = minimist(strToParse.split(' '))
|
||||
const fArg = 'f'
|
||||
const filenameArg = 'filename'
|
||||
let start = strToParse.indexOf('-filename')
|
||||
let offset = 7
|
||||
|
||||
fileNames.push(...this.extractFilesFromMinimist(argv, fArg))
|
||||
fileNames.push(...this.extractFilesFromMinimist(argv, filenameArg))
|
||||
if (start == -1) {
|
||||
start = strToParse.indexOf('-f')
|
||||
|
||||
return fileNames.join(' ')
|
||||
}
|
||||
|
||||
private extractFilesFromMinimist(argv, arg: string): string[] {
|
||||
if (!argv[arg]) {
|
||||
return []
|
||||
}
|
||||
const toReturn: string[] = []
|
||||
if (typeof argv[arg] === 'string') {
|
||||
toReturn.push(...argv[arg].split(','))
|
||||
} else {
|
||||
for (const value of argv[arg] as string[]) {
|
||||
toReturn.push(...value.split(','))
|
||||
if (start == -1) {
|
||||
return ''
|
||||
}
|
||||
offset = 0
|
||||
}
|
||||
|
||||
return toReturn
|
||||
let temp = strToParse.substring(start + offset)
|
||||
let end = temp.indexOf(' -')
|
||||
|
||||
//End could be case where the -f flag was last, or -f is followed by some additonal flag and it's arguments
|
||||
return temp.substring(3, end == -1 ? temp.length : end).trim()
|
||||
}
|
||||
|
||||
private containsFilenames(str: string) {
|
||||
|
||||
@@ -1,45 +1,11 @@
|
||||
import {
|
||||
getFilesFromDirectoriesAndURLs,
|
||||
getTempDirectory,
|
||||
urlFileKind,
|
||||
writeYamlFromURLToFile
|
||||
} from './fileUtils'
|
||||
import {getFilesFromDirectories} from './fileUtils'
|
||||
|
||||
import * as yaml from 'js-yaml'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import {succeeded} from '../types/errorable'
|
||||
|
||||
const sampleYamlUrl =
|
||||
'https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/controllers/nginx-deployment.yaml'
|
||||
describe('File utils', () => {
|
||||
test('correctly parses a yaml file from a URL', async () => {
|
||||
const tempFile = await writeYamlFromURLToFile(sampleYamlUrl, 0)
|
||||
const fileContents = fs.readFileSync(tempFile).toString()
|
||||
const inputObjects = yaml.safeLoadAll(fileContents)
|
||||
expect(inputObjects).toHaveLength(1)
|
||||
|
||||
for (const obj of inputObjects) {
|
||||
expect(obj.metadata.name).toBe('nginx-deployment')
|
||||
expect(obj.kind).toBe('Deployment')
|
||||
}
|
||||
})
|
||||
|
||||
it('fails when a bad URL is given among other files', async () => {
|
||||
const badUrl = 'https://www.github.com'
|
||||
|
||||
it('detects files in nested directories and ignores non-manifest files and empty dirs', () => {
|
||||
const testPath = path.join('test', 'unit', 'manifests')
|
||||
await expect(
|
||||
getFilesFromDirectoriesAndURLs([testPath, badUrl])
|
||||
).rejects.toThrow()
|
||||
})
|
||||
|
||||
it('detects files in nested directories and ignores non-manifest files and empty dirs', async () => {
|
||||
const testPath = path.join('test', 'unit', 'manifests')
|
||||
const testSearch: string[] = await getFilesFromDirectoriesAndURLs([
|
||||
testPath,
|
||||
sampleYamlUrl
|
||||
])
|
||||
const testSearch: string[] = getFilesFromDirectories([testPath])
|
||||
|
||||
const expectedManifests = [
|
||||
'test/unit/manifests/manifest_test_dir/another_layer/deep-ingress.yaml',
|
||||
@@ -51,18 +17,13 @@ describe('File utils', () => {
|
||||
]
|
||||
|
||||
// is there a more efficient way to test equality w random order?
|
||||
expect(testSearch).toHaveLength(8)
|
||||
expect(testSearch).toHaveLength(7)
|
||||
expectedManifests.forEach((fileName) => {
|
||||
if (fileName.startsWith('test/unit')) {
|
||||
expect(testSearch).toContain(fileName)
|
||||
} else {
|
||||
expect(fileName.includes(urlFileKind)).toBe(true)
|
||||
expect(fileName.startsWith(getTempDirectory()))
|
||||
}
|
||||
expect(testSearch).toContain(fileName)
|
||||
})
|
||||
})
|
||||
|
||||
it('crashes when an invalid file is provided', async () => {
|
||||
it('crashes when an invalid file is provided', () => {
|
||||
const badPath = path.join('test', 'unit', 'manifests', 'nonexistent.yaml')
|
||||
const goodPath = path.join(
|
||||
'test',
|
||||
@@ -71,12 +32,12 @@ describe('File utils', () => {
|
||||
'manifest_test_dir'
|
||||
)
|
||||
|
||||
expect(
|
||||
getFilesFromDirectoriesAndURLs([badPath, goodPath])
|
||||
).rejects.toThrowError()
|
||||
expect(() => {
|
||||
getFilesFromDirectories([badPath, goodPath])
|
||||
}).toThrowError()
|
||||
})
|
||||
|
||||
it("doesn't duplicate files when nested dir included", async () => {
|
||||
it("doesn't duplicate files when nested dir included", () => {
|
||||
const outerPath = path.join('test', 'unit', 'manifests')
|
||||
const fileAtOuter = path.join(
|
||||
'test',
|
||||
@@ -92,16 +53,11 @@ describe('File utils', () => {
|
||||
)
|
||||
|
||||
expect(
|
||||
await getFilesFromDirectoriesAndURLs([
|
||||
outerPath,
|
||||
fileAtOuter,
|
||||
innerPath
|
||||
])
|
||||
getFilesFromDirectories([outerPath, fileAtOuter, innerPath])
|
||||
).toHaveLength(7)
|
||||
})
|
||||
|
||||
it('throws an error for an invalid URL', async () => {
|
||||
const badUrl = 'https://www.github.com'
|
||||
await expect(writeYamlFromURLToFile(badUrl, 0)).rejects.toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
// files that don't exist / nested files that don't exist / something else with non-manifest
|
||||
// lots of combinations of pointing to a directory and non yaml/yaml file
|
||||
// similarly named files in different folders
|
||||
|
||||
+4
-103
@@ -1,15 +1,8 @@
|
||||
import * as fs from 'fs'
|
||||
import * as https from 'https'
|
||||
import * as path from 'path'
|
||||
import * as core from '@actions/core'
|
||||
import * as os from 'os'
|
||||
import * as yaml from 'js-yaml'
|
||||
import {Errorable, succeeded, failed, Failed} from '../types/errorable'
|
||||
import {getCurrentTime} from './timeUtils'
|
||||
import {isHttpUrl} from './githubUtils'
|
||||
import {K8sObject} from '../types/k8sObject'
|
||||
|
||||
export const urlFileKind = 'urlfile'
|
||||
|
||||
export function getTempDirectory(): string {
|
||||
return process.env['runner.tempDirectory'] || os.tmpdir()
|
||||
@@ -69,27 +62,12 @@ function getManifestFileName(kind: string, name: string) {
|
||||
return path.join(tempDirectory, path.basename(filePath))
|
||||
}
|
||||
|
||||
export async function getFilesFromDirectoriesAndURLs(
|
||||
filePaths: string[]
|
||||
): Promise<string[]> {
|
||||
export function getFilesFromDirectories(filePaths: string[]): string[] {
|
||||
const fullPathSet: Set<string> = new Set<string>()
|
||||
|
||||
let fileCounter = 0
|
||||
for (const fileName of filePaths) {
|
||||
filePaths.forEach((fileName) => {
|
||||
try {
|
||||
if (isHttpUrl(fileName)) {
|
||||
try {
|
||||
const tempFilePath: string = await writeYamlFromURLToFile(
|
||||
fileName,
|
||||
fileCounter++
|
||||
)
|
||||
fullPathSet.add(tempFilePath)
|
||||
} catch (e) {
|
||||
throw Error(
|
||||
`encountered error trying to pull YAML from URL ${fileName}: ${e}`
|
||||
)
|
||||
}
|
||||
} else if (fs.lstatSync(fileName).isDirectory()) {
|
||||
if (fs.lstatSync(fileName).isDirectory()) {
|
||||
recurisveManifestGetter(fileName).forEach((file) => {
|
||||
fullPathSet.add(file)
|
||||
})
|
||||
@@ -108,86 +86,9 @@ export async function getFilesFromDirectoriesAndURLs(
|
||||
`Exception occurred while reading the file ${fileName}: ${ex}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const arr = Array.from(fullPathSet)
|
||||
return arr
|
||||
}
|
||||
|
||||
export async function writeYamlFromURLToFile(
|
||||
url: string,
|
||||
fileNumber: number
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
https
|
||||
.get(url, async (response) => {
|
||||
const code = response.statusCode ?? 0
|
||||
if (code >= 400) {
|
||||
reject(
|
||||
Error(
|
||||
`received response status ${response.statusMessage} from url ${url}`
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const targetPath = getManifestFileName(
|
||||
urlFileKind,
|
||||
fileNumber.toString()
|
||||
)
|
||||
// save the file to disk
|
||||
const fileWriter = fs
|
||||
.createWriteStream(targetPath)
|
||||
.on('finish', () => {
|
||||
const verification = verifyYaml(targetPath, url)
|
||||
if (succeeded(verification)) {
|
||||
core.debug(
|
||||
`outputting YAML contents from ${url} to ${targetPath}: ${JSON.stringify(
|
||||
verification.result
|
||||
)}`
|
||||
)
|
||||
resolve(targetPath)
|
||||
} else {
|
||||
reject(verification.error)
|
||||
}
|
||||
})
|
||||
|
||||
response.pipe(fileWriter)
|
||||
})
|
||||
.on('error', (error) => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function verifyYaml(filepath: string, url: string): Errorable<K8sObject[]> {
|
||||
const fileContents = fs.readFileSync(filepath).toString()
|
||||
let inputObjects
|
||||
try {
|
||||
inputObjects = yaml.safeLoadAll(fileContents)
|
||||
} catch (e) {
|
||||
return {
|
||||
succeeded: false,
|
||||
error: `failed to parse manifest from url ${url}: ${e}`
|
||||
}
|
||||
}
|
||||
|
||||
if (!inputObjects || inputObjects.length == 0) {
|
||||
return {
|
||||
succeeded: false,
|
||||
error: `failed to parse manifest from url ${url}: no objects detected in manifest`
|
||||
}
|
||||
}
|
||||
|
||||
for (const obj of inputObjects) {
|
||||
if (!obj.kind || !obj.apiVersion || !obj.metadata) {
|
||||
return {
|
||||
succeeded: false,
|
||||
error: `failed to parse manifest from ${url}: missing fields`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {succeeded: true, result: inputObjects}
|
||||
return Array.from(fullPathSet)
|
||||
}
|
||||
|
||||
function recurisveManifestGetter(dirName: string): string[] {
|
||||
|
||||
@@ -2,8 +2,6 @@ import * as core from '@actions/core'
|
||||
import {ExecOutput} from '@actions/exec'
|
||||
import {Kubectl} from '../types/kubectl'
|
||||
|
||||
const NAMESPACE = 'namespace'
|
||||
|
||||
export function checkForErrors(
|
||||
execResults: ExecOutput[],
|
||||
warnIfError?: boolean
|
||||
@@ -32,12 +30,7 @@ export async function getLastSuccessfulRunSha(
|
||||
annotationKey: string
|
||||
): Promise<string> {
|
||||
try {
|
||||
const result = await kubectl.getResource(
|
||||
NAMESPACE,
|
||||
namespaceName,
|
||||
false,
|
||||
namespaceName
|
||||
)
|
||||
const result = await kubectl.getResource('namespace', namespaceName)
|
||||
if (result?.stderr) {
|
||||
core.warning(result.stderr)
|
||||
return process.env.GITHUB_SHA
|
||||
@@ -60,23 +53,15 @@ export async function annotateChildPods(
|
||||
kubectl: Kubectl,
|
||||
resourceType: string,
|
||||
resourceName: string,
|
||||
namespace: string | undefined,
|
||||
annotationKeyValStr: string
|
||||
annotationKeyValStr: string,
|
||||
allPods
|
||||
): Promise<ExecOutput[]> {
|
||||
let owner = resourceName
|
||||
if (resourceType.toLowerCase().indexOf('deployment') > -1) {
|
||||
owner = await kubectl.getNewReplicaSet(resourceName, namespace)
|
||||
owner = await kubectl.getNewReplicaSet(resourceName)
|
||||
}
|
||||
|
||||
const commandExecutionResults = []
|
||||
|
||||
let allPods
|
||||
try {
|
||||
allPods = JSON.parse((await kubectl.getAllPods()).stdout)
|
||||
} catch (e) {
|
||||
core.debug(`Unable to parse pods: ${e}`)
|
||||
}
|
||||
|
||||
if (allPods?.items && allPods.items?.length > 0) {
|
||||
allPods.items.forEach((pod) => {
|
||||
const owners = pod?.metadata?.ownerReferences
|
||||
@@ -87,8 +72,7 @@ export async function annotateChildPods(
|
||||
kubectl.annotate(
|
||||
'pod',
|
||||
pod.metadata.name,
|
||||
annotationKeyValStr,
|
||||
namespace
|
||||
annotationKeyValStr
|
||||
)
|
||||
)
|
||||
break
|
||||
|
||||
@@ -4,9 +4,6 @@ import {Kubectl, Resource} from '../types/kubectl'
|
||||
import {checkForErrors} from './kubectlUtils'
|
||||
import {sleep} from './timeUtils'
|
||||
|
||||
const IS_SILENT = false
|
||||
const POD = 'pod'
|
||||
|
||||
export async function checkManifestStability(
|
||||
kubectl: Kubectl,
|
||||
resources: Resource[]
|
||||
@@ -23,35 +20,24 @@ export async function checkManifestStability(
|
||||
try {
|
||||
const result = await kubectl.checkRolloutStatus(
|
||||
resource.type,
|
||||
resource.name,
|
||||
resource.namespace
|
||||
resource.name
|
||||
)
|
||||
checkForErrors([result])
|
||||
} catch (ex) {
|
||||
core.error(ex)
|
||||
await kubectl.describe(
|
||||
resource.type,
|
||||
resource.name,
|
||||
IS_SILENT,
|
||||
resource.namespace
|
||||
)
|
||||
await kubectl.describe(resource.type, resource.name)
|
||||
rolloutStatusHasErrors = true
|
||||
}
|
||||
}
|
||||
|
||||
if (resource.type == KubernetesConstants.KubernetesWorkload.POD) {
|
||||
try {
|
||||
await checkPodStatus(kubectl, resource)
|
||||
await checkPodStatus(kubectl, resource.name)
|
||||
} catch (ex) {
|
||||
core.warning(
|
||||
`Could not determine pod status: ${JSON.stringify(ex)}`
|
||||
)
|
||||
await kubectl.describe(
|
||||
resource.type,
|
||||
resource.name,
|
||||
IS_SILENT,
|
||||
resource.namespace
|
||||
)
|
||||
await kubectl.describe(resource.type, resource.name)
|
||||
}
|
||||
}
|
||||
if (
|
||||
@@ -59,11 +45,14 @@ export async function checkManifestStability(
|
||||
KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE
|
||||
) {
|
||||
try {
|
||||
const service = await getService(kubectl, resource)
|
||||
const service = await getService(kubectl, resource.name)
|
||||
const {spec, status} = service
|
||||
if (spec.type === KubernetesConstants.ServiceTypes.LOAD_BALANCER) {
|
||||
if (!isLoadBalancerIPAssigned(status)) {
|
||||
await waitForServiceExternalIPAssignment(kubectl, resource)
|
||||
await waitForServiceExternalIPAssignment(
|
||||
kubectl,
|
||||
resource.name
|
||||
)
|
||||
} else {
|
||||
core.info(
|
||||
`ServiceExternalIP ${resource.name} ${status.loadBalancer.ingress[0].ip}`
|
||||
@@ -74,12 +63,7 @@ export async function checkManifestStability(
|
||||
core.warning(
|
||||
`Could not determine service status of: ${resource.name} Error: ${ex}`
|
||||
)
|
||||
await kubectl.describe(
|
||||
resource.type,
|
||||
resource.name,
|
||||
IS_SILENT,
|
||||
resource.namespace
|
||||
)
|
||||
await kubectl.describe(resource.type, resource.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,7 +75,7 @@ export async function checkManifestStability(
|
||||
|
||||
export async function checkPodStatus(
|
||||
kubectl: Kubectl,
|
||||
pod: Resource
|
||||
podName: string
|
||||
): Promise<void> {
|
||||
const sleepTimeout = 10 * 1000 // 10 seconds
|
||||
const iterations = 60 // 60 * 10 seconds timeout = 10 minutes max timeout
|
||||
@@ -101,8 +85,8 @@ export async function checkPodStatus(
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
await sleep(sleepTimeout)
|
||||
|
||||
core.debug(`Polling for pod status: ${pod.name}`)
|
||||
podStatus = await getPodStatus(kubectl, pod)
|
||||
core.debug(`Polling for pod status: ${podName}`)
|
||||
podStatus = await getPodStatus(kubectl, podName)
|
||||
|
||||
if (
|
||||
podStatus &&
|
||||
@@ -113,42 +97,37 @@ export async function checkPodStatus(
|
||||
}
|
||||
}
|
||||
|
||||
podStatus = await getPodStatus(kubectl, pod)
|
||||
podStatus = await getPodStatus(kubectl, podName)
|
||||
switch (podStatus.phase) {
|
||||
case 'Succeeded':
|
||||
case 'Running':
|
||||
if (isPodReady(podStatus)) {
|
||||
console.log(`pod/${pod.name} is successfully rolled out`)
|
||||
console.log(`pod/${podName} is successfully rolled out`)
|
||||
} else {
|
||||
kubectlDescribeNeeded = true
|
||||
}
|
||||
break
|
||||
case 'Pending':
|
||||
if (!isPodReady(podStatus)) {
|
||||
core.warning(`pod/${pod.name} rollout status check timed out`)
|
||||
core.warning(`pod/${podName} rollout status check timed out`)
|
||||
kubectlDescribeNeeded = true
|
||||
}
|
||||
break
|
||||
case 'Failed':
|
||||
core.error(`pod/${pod.name} rollout failed`)
|
||||
core.error(`pod/${podName} rollout failed`)
|
||||
kubectlDescribeNeeded = true
|
||||
break
|
||||
default:
|
||||
core.warning(`pod/${pod.name} rollout status: ${podStatus.phase}`)
|
||||
core.warning(`pod/${podName} rollout status: ${podStatus.phase}`)
|
||||
}
|
||||
|
||||
if (kubectlDescribeNeeded) {
|
||||
await kubectl.describe(POD, pod.name, IS_SILENT, pod.namespace)
|
||||
await kubectl.describe('pod', podName)
|
||||
}
|
||||
}
|
||||
|
||||
async function getPodStatus(kubectl: Kubectl, pod: Resource) {
|
||||
const podResult = await kubectl.getResource(
|
||||
POD,
|
||||
pod.name,
|
||||
IS_SILENT,
|
||||
pod.namespace
|
||||
)
|
||||
async function getPodStatus(kubectl: Kubectl, podName: string) {
|
||||
const podResult = await kubectl.getResource('pod', podName)
|
||||
checkForErrors([podResult])
|
||||
|
||||
return JSON.parse(podResult.stdout).status
|
||||
@@ -172,12 +151,10 @@ function isPodReady(podStatus: any): boolean {
|
||||
return allContainersAreReady
|
||||
}
|
||||
|
||||
async function getService(kubectl: Kubectl, service: Resource) {
|
||||
async function getService(kubectl: Kubectl, serviceName) {
|
||||
const serviceResult = await kubectl.getResource(
|
||||
KubernetesConstants.DiscoveryAndLoadBalancerResource.SERVICE,
|
||||
service.name,
|
||||
IS_SILENT,
|
||||
service.namespace
|
||||
serviceName
|
||||
)
|
||||
|
||||
checkForErrors([serviceResult])
|
||||
@@ -186,25 +163,25 @@ async function getService(kubectl: Kubectl, service: Resource) {
|
||||
|
||||
async function waitForServiceExternalIPAssignment(
|
||||
kubectl: Kubectl,
|
||||
service: Resource
|
||||
serviceName: string
|
||||
): Promise<void> {
|
||||
const sleepTimeout = 10 * 1000 // 10 seconds
|
||||
const iterations = 18 // 18 * 10 seconds timeout = 3 minutes max timeout
|
||||
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
core.info(`Wait for service ip assignment : ${service.name}`)
|
||||
core.info(`Wait for service ip assignment : ${serviceName}`)
|
||||
await sleep(sleepTimeout)
|
||||
|
||||
const status = (await getService(kubectl, service)).status
|
||||
const status = (await getService(kubectl, serviceName)).status
|
||||
if (isLoadBalancerIPAssigned(status)) {
|
||||
core.info(
|
||||
`ServiceExternalIP ${service.name} ${status.loadBalancer.ingress[0].ip}`
|
||||
`ServiceExternalIP ${serviceName} ${status.loadBalancer.ingress[0].ip}`
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
core.warning(`Wait for service ip assignment timed out ${service.name}`)
|
||||
core.warning(`Wait for service ip assignment timed out${serviceName}`)
|
||||
}
|
||||
|
||||
function isLoadBalancerIPAssigned(status: any) {
|
||||
|
||||
@@ -280,8 +280,7 @@ export function getResources(
|
||||
) {
|
||||
resources.push({
|
||||
type: inputObject.kind,
|
||||
name: inputObject.metadata.name,
|
||||
namespace: inputObject?.metadata?.namespace
|
||||
name: inputObject.metadata.name
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
cleanLabel,
|
||||
removeInvalidLabelCharacters,
|
||||
VALID_LABEL_REGEX
|
||||
} from '../utilities/workflowAnnotationUtils'
|
||||
import {cleanLabel} from '../utilities/workflowAnnotationUtils'
|
||||
|
||||
describe('WorkflowAnnotationUtils', () => {
|
||||
describe('cleanLabel', () => {
|
||||
@@ -20,14 +16,5 @@ describe('WorkflowAnnotationUtils', () => {
|
||||
cleanLabel('Workflow Name / With Slashes / And Spaces')
|
||||
).toEqual('Workflow_Name_-_With_Slashes_-_And_Spaces')
|
||||
})
|
||||
it('should return a blank string when regex fails (https://github.com/Azure/k8s-deploy/issues/266)', () => {
|
||||
const label = '持续部署'
|
||||
expect(cleanLabel(label)).toEqual('github-workflow-file')
|
||||
|
||||
let removedInvalidChars = removeInvalidLabelCharacters(label)
|
||||
|
||||
const regexResult = VALID_LABEL_REGEX.exec(removedInvalidChars)
|
||||
expect(regexResult).toBe(null)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,8 +2,6 @@ import {DeploymentConfig} from '../types/deploymentConfig'
|
||||
|
||||
const ANNOTATION_PREFIX = 'actions.github.com'
|
||||
|
||||
export const VALID_LABEL_REGEX = /([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]/
|
||||
|
||||
export function getWorkflowAnnotations(
|
||||
lastSuccessRunSha: string,
|
||||
workflowFilePath: string,
|
||||
@@ -39,17 +37,11 @@ export function getWorkflowAnnotationKeyLabel(): string {
|
||||
* @returns cleaned label
|
||||
*/
|
||||
export function cleanLabel(label: string): string {
|
||||
let removedInvalidChars = removeInvalidLabelCharacters(label)
|
||||
|
||||
const regexResult = VALID_LABEL_REGEX.exec(removedInvalidChars) || [
|
||||
'github-workflow-file'
|
||||
]
|
||||
return regexResult[0]
|
||||
}
|
||||
|
||||
export function removeInvalidLabelCharacters(label: string): string {
|
||||
return label
|
||||
let removedInvalidChars = label
|
||||
.replace(/\s/gi, '_')
|
||||
.replace(/[\/\\\|]/gi, '-')
|
||||
.replace(/[^-A-Za-z0-9_.]/gi, '')
|
||||
|
||||
const regex = /([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]/
|
||||
return regex.exec(removedInvalidChars)[0] || ''
|
||||
}
|
||||
|
||||
@@ -1,29 +1,12 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import subprocess, sys
|
||||
|
||||
kind = sys.argv[1]
|
||||
name = sys.argv[2]
|
||||
namespace = 'test-' + sys.argv[3]
|
||||
|
||||
def delete(kind, name, namespace):
|
||||
try:
|
||||
if (name == "all"):
|
||||
print('kubectl delete --all' + kind + ' -n ' + namespace)
|
||||
deletion = subprocess.Popen(
|
||||
['kubectl', 'delete', kind, '--all', '--namespace', namespace])
|
||||
result, err = deletion.communicate()
|
||||
else:
|
||||
print('kubectl delete ' + kind + ' ' + name + ' -n ' + namespace)
|
||||
deletion = subprocess.Popen(
|
||||
['kubectl', 'delete', kind, name, '--namespace', namespace])
|
||||
result, err = deletion.communicate()
|
||||
except Exception as ex:
|
||||
print('Error occured during deletion', ex)
|
||||
|
||||
|
||||
def main():
|
||||
kind = sys.argv[1]
|
||||
name = sys.argv[2]
|
||||
namespace = sys.argv[3]
|
||||
delete(kind, name, namespace)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
try:
|
||||
print('kubectl delete ' + kind + ' ' + name + ' -n ' + namespace)
|
||||
deletion = subprocess.Popen(['kubectl', 'delete', kind, name, '--namespace', namespace])
|
||||
result, err = deletion.communicate()
|
||||
except Exception as ex:
|
||||
print('Error occured during deletion', ex)
|
||||
|
||||
@@ -1,258 +1,33 @@
|
||||
from operator import truediv
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from unicodedata import name
|
||||
import os, sys, json
|
||||
|
||||
# This integration test is used to confirm that k8s resources of a specified name, type, and configuration have been deployed.
|
||||
# Expected configurations are fed into the python script as command-line arguments and are compared to the configuration of resources that have been deployed.
|
||||
RESULT = 'false'
|
||||
k8_object = None
|
||||
kind = sys.argv[1]
|
||||
name = sys.argv[2]
|
||||
color = sys.argv[3]
|
||||
namespace = 'test-' + sys.argv[4]
|
||||
|
||||
# args will be formatted like labels=testkey:testValue,otherKey=otherValue
|
||||
# or for singular ones, just with containerName=container
|
||||
print('kubectl get '+kind+' '+name+' -n '+namespace+' -o json')
|
||||
|
||||
try:
|
||||
k8_object = json.load(os.popen('kubectl get '+kind+' '+name+' -n '+namespace+' -o json'))
|
||||
except:
|
||||
sys.exit(kind+' '+name+' not created')
|
||||
|
||||
kindKey = "kind"
|
||||
nameKey = "name"
|
||||
containerKey = "containerName"
|
||||
labelsKey = "labels"
|
||||
annotationsKey = "annotations"
|
||||
selectorLabelsKey = "selectorLabels"
|
||||
namespaceKey = "namespace"
|
||||
ingressServicesKey = "ingressServices"
|
||||
tsServicesKey = "tsServices"
|
||||
privateKey = "private"
|
||||
try:
|
||||
if kind == 'Deployment' and k8_object['spec']['selector']['matchLabels']['k8s.deploy.color'] == str(color):
|
||||
RESULT = 'true'
|
||||
if kind == 'Service' and k8_object['spec']['selector']['k8s.deploy.color'] == str(color):
|
||||
RESULT = 'true'
|
||||
if kind == 'Ingress':
|
||||
suffix = ''
|
||||
if str(color) == 'green':
|
||||
suffix = '-green'
|
||||
if k8_object['spec']['rules'][0]['http']['paths'][0]['backend']['serviceName']=='nginx-service'+suffix and k8_object['spec']['rules'][0]['http']['paths'][1]['backend']['serviceName']=='unrouted-service':
|
||||
RESULT = 'true'
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def parseArgs(sysArgs):
|
||||
argsDict = stringListToDict(sysArgs, "=")
|
||||
|
||||
# mandatory parameters
|
||||
if not kindKey in argsDict:
|
||||
raise ValueError(f"missing key: {kindKey}")
|
||||
|
||||
if not nameKey in argsDict:
|
||||
raise ValueError(f"missing key: {nameKey}")
|
||||
|
||||
if not namespaceKey in argsDict:
|
||||
raise ValueError(f"missing key: {namespaceKey}")
|
||||
|
||||
# reformat map-like parameters (eg, paramName=key1:value1,key2:value2)
|
||||
if labelsKey in argsDict:
|
||||
argsDict[labelsKey] = stringListToDict(
|
||||
argsDict[labelsKey].split(","), ":")
|
||||
|
||||
if selectorLabelsKey in argsDict:
|
||||
argsDict[selectorLabelsKey] = stringListToDict(
|
||||
argsDict[selectorLabelsKey].split(","), ":")
|
||||
|
||||
if tsServicesKey in argsDict:
|
||||
argsDict[tsServicesKey] = stringListToDict(
|
||||
argsDict[tsServicesKey].split(","), ":")
|
||||
|
||||
for key in argsDict[tsServicesKey]:
|
||||
argsDict[tsServicesKey][key] = int(argsDict[tsServicesKey][key])
|
||||
|
||||
# reformat list-like parameters (eg, paramName=value1,value2,value3)
|
||||
if ingressServicesKey in argsDict:
|
||||
argsDict[ingressServicesKey] = argsDict[ingressServicesKey].split(",")
|
||||
|
||||
if annotationsKey in argsDict:
|
||||
argsDict[annotationsKey] = argsDict[annotationsKey].split(",")
|
||||
|
||||
return argsDict
|
||||
|
||||
|
||||
def stringListToDict(args: list[str], separator: str):
|
||||
parsedArgs = {}
|
||||
for arg in args:
|
||||
print(f"parsing arg {arg}")
|
||||
argSplit = arg.split(separator)
|
||||
parsedArgs[argSplit[0]] = argSplit[1]
|
||||
|
||||
return parsedArgs
|
||||
|
||||
|
||||
def verifyDeployment(deployment, parsedArgs):
|
||||
# test container image, labels, annotations, selector labels
|
||||
if not containerKey in parsedArgs:
|
||||
raise ValueError(
|
||||
f"expected container image name not provided to inspect deployment {parsedArgs[nameKey]}")
|
||||
|
||||
actualImageName = deployment['spec']['template']['spec']['containers'][0]['image']
|
||||
if not actualImageName == parsedArgs[containerKey]:
|
||||
return False, f"expected container image name {parsedArgs[containerKey]} but got {actualImageName} instead"
|
||||
|
||||
if not selectorLabelsKey in parsedArgs:
|
||||
raise ValueError(
|
||||
f"expected selector labels not provided to inspect deployment {parsedArgs[nameKey]}")
|
||||
dictMatch, msg = compareDicts(
|
||||
deployment['spec']['selector']['matchLabels'], parsedArgs[selectorLabelsKey], selectorLabelsKey)
|
||||
if not dictMatch:
|
||||
return dictMatch, msg
|
||||
|
||||
if labelsKey in parsedArgs:
|
||||
dictMatch, msg = compareDicts(
|
||||
deployment['metadata']['labels'], parsedArgs[labelsKey], labelsKey)
|
||||
if not dictMatch:
|
||||
return dictMatch, msg
|
||||
|
||||
if annotationsKey in parsedArgs:
|
||||
if len(parsedArgs[annotationsKey]) != len(deployment['metadata']['annotations']):
|
||||
return False, f"expected {len(parsedArgs[annotationsKey])} annotations but found {len(deployment['metadata']['annotations'])}"
|
||||
keysPresent, msg = validateKeyPresence(
|
||||
deployment['metadata']['annotations'], parsedArgs[annotationsKey])
|
||||
if not keysPresent:
|
||||
return keysPresent, msg
|
||||
return True, ""
|
||||
|
||||
def verifyService(service, parsedArgs):
|
||||
# test selector labels, labels, annotations
|
||||
if not selectorLabelsKey in parsedArgs:
|
||||
raise ValueError(
|
||||
f"expected selector labels not provided to inspect service {parsedArgs[nameKey]}")
|
||||
dictMatch, msg = compareDicts(
|
||||
service['spec']['selector'], parsedArgs[selectorLabelsKey], selectorLabelsKey)
|
||||
if not dictMatch:
|
||||
return dictMatch, msg
|
||||
|
||||
if labelsKey in parsedArgs:
|
||||
print(f" service is {service}")
|
||||
dictMatch, msg = compareDicts(
|
||||
service['metadata']['labels'], parsedArgs[labelsKey], labelsKey)
|
||||
if not dictMatch:
|
||||
return dictMatch, msg
|
||||
|
||||
if annotationsKey in parsedArgs:
|
||||
keysPresent, msg = validateKeyPresence(
|
||||
service['metadata']['annotations'], parsedArgs[annotationsKey])
|
||||
if not keysPresent:
|
||||
return keysPresent, msg
|
||||
|
||||
return True, ""
|
||||
|
||||
|
||||
def verifyIngress(ingress, parsedArgs):
|
||||
# test services in paths
|
||||
if not ingressServicesKey in parsedArgs:
|
||||
raise ValueError(
|
||||
f"expected services not provided to inspect ingress {parsedArgs[nameKey]}")
|
||||
|
||||
expectedIngresses = parsedArgs[ingressServicesKey]
|
||||
for i in range(len(ingress['spec']['rules'][0]['http']['paths'])):
|
||||
print(
|
||||
f"service obj is {ingress['spec']['rules'][0]['http']['paths'][i]}")
|
||||
svcName = ingress['spec']['rules'][0]['http']['paths'][i]['backend']['service']['name']
|
||||
if svcName != expectedIngresses[i]:
|
||||
return False, f"for ingress {parsedArgs[nameKey]} expected svc name {expectedIngresses[i]} at position {i} but got {svcName}"
|
||||
|
||||
return True, ""
|
||||
|
||||
|
||||
def verifyTSObject(tsObj, parsedArgs):
|
||||
if not tsServicesKey in parsedArgs:
|
||||
raise ValueError(
|
||||
f"expected services not provided to inspect ts object {parsedArgs[nameKey]}")
|
||||
|
||||
expectedServices = parsedArgs[tsServicesKey]
|
||||
actualServices = {}
|
||||
backends = tsObj['spec']['backends']
|
||||
for i in range(len(backends)):
|
||||
svcName = backends[i]['service']
|
||||
svcWeight = int(backends[i]['weight'])
|
||||
actualServices[svcName] = svcWeight
|
||||
|
||||
dictResult, msg = compareDicts(
|
||||
actualServices, expectedServices, tsServicesKey)
|
||||
if not dictResult:
|
||||
return False, msg
|
||||
|
||||
return True, ""
|
||||
|
||||
|
||||
def compareDicts(actual: dict, expected: dict, paramName=""):
|
||||
actualKeys = actual.keys()
|
||||
expectedKeys = expected.keys()
|
||||
|
||||
if not actualKeys == expectedKeys:
|
||||
msg = f'dicts had different keys.\n actual: {actual}\n expected: {expected}'
|
||||
if not paramName == "":
|
||||
msg = f"for param {paramName}, " + msg
|
||||
return False, msg
|
||||
for key in actualKeys:
|
||||
if not actual[key] == expected[key]:
|
||||
msg = f'dicts differed at key {key}.\n actual[{key}] is {actual[key]} and expected[{key}] is {expected[key]}'
|
||||
if not paramName == "":
|
||||
msg = f"for param {paramName}, " + msg
|
||||
return False, msg
|
||||
|
||||
return True, ""
|
||||
|
||||
def validateKeyPresence(actualDict: dict, expectedKeys: list):
|
||||
actualKeys = actualDict.keys()
|
||||
for key in expectedKeys:
|
||||
if key not in actualKeys:
|
||||
return False, f"expected key {key} not found in actual dict. \n actual dict keys {','.join(actualKeys)}"
|
||||
|
||||
return True, ""
|
||||
|
||||
def main():
|
||||
parsedArgs: dict = parseArgs(sys.argv[1:])
|
||||
RESULT = False
|
||||
msg = "unknown type (no verification method currently exists)"
|
||||
k8_object = None
|
||||
|
||||
kind = parsedArgs[kindKey]
|
||||
name = parsedArgs[nameKey]
|
||||
namespace = parsedArgs[namespaceKey]
|
||||
cmd = 'kubectl get '+kind + ' '+name+' -n '+namespace+' -o json'
|
||||
|
||||
k8s_object = None
|
||||
azPrefix = ""
|
||||
try:
|
||||
if privateKey in parsedArgs:
|
||||
uniqueName = parsedArgs[privateKey]
|
||||
azPrefix = f"az aks command invoke --resource-group {uniqueName} --name {uniqueName} --command "
|
||||
cmd = azPrefix + "'" + cmd + "'"
|
||||
outputString = os.popen(cmd).read()
|
||||
successExit = "exitcode=0"
|
||||
if successExit not in outputString:
|
||||
raise ValueError(f"private cluster get failed for {kind} {name}")
|
||||
|
||||
objString = outputString.split(successExit)[1]
|
||||
k8_object = json.loads(objString)
|
||||
|
||||
else:
|
||||
k8_object = json.load(os.popen(cmd))
|
||||
|
||||
if k8_object == None:
|
||||
raise ValueError(f"{kind} {name} was not found")
|
||||
|
||||
except:
|
||||
msg = kind+' '+name+' not created or not found'
|
||||
getAllObjectsCmd = azPrefix + 'kubectl get '+kind+' -n '+namespace
|
||||
if not azPrefix == "":
|
||||
getAllObjectsCmd = azPrefix + "'{getAllObjectsCmd}'" # add extra set of quotes
|
||||
cmd = + "'" + cmd + "'"
|
||||
foundObjects = os.popen().read()
|
||||
suffix = f"resources of type {kind}: {foundObjects}"
|
||||
sys.exit(msg + " " + suffix)
|
||||
|
||||
if kind == 'Deployment':
|
||||
RESULT, msg = verifyDeployment(
|
||||
k8_object, parsedArgs)
|
||||
if kind == 'Service':
|
||||
RESULT, msg = verifyService(
|
||||
k8_object, parsedArgs)
|
||||
if kind == 'Ingress':
|
||||
RESULT, msg = verifyIngress(k8_object, parsedArgs)
|
||||
if kind == "TrafficSplit":
|
||||
RESULT, msg = verifyTSObject(k8_object, parsedArgs)
|
||||
|
||||
if not RESULT:
|
||||
sys.exit(f"{kind} {name} failed check: {msg}")
|
||||
|
||||
print('Test passed')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
if RESULT=='false':
|
||||
sys.exit(kind+' '+name+' not labelled properly')
|
||||
print('Test passed')
|
||||
|
||||
+6
-12
@@ -16,7 +16,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
@@ -32,7 +32,7 @@ spec:
|
||||
port: 80
|
||||
targetPort: 80
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx-ingress
|
||||
@@ -43,16 +43,10 @@ spec:
|
||||
- http:
|
||||
paths:
|
||||
- path: /testpath
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-service
|
||||
port:
|
||||
number: 80
|
||||
serviceName: nginx-service
|
||||
servicePort: 80
|
||||
- path: /testpath2
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: unrouted-service
|
||||
port:
|
||||
number: 80
|
||||
serviceName: unrouted-service
|
||||
servicePort: 80
|
||||
+1
-1
@@ -16,7 +16,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
image: nginx:1.14.2
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
@@ -1,33 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
Reference in New Issue
Block a user