mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-06-21 10:39:26 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 42f5b49202 | |||
| b9a9965750 | |||
| 47445fb82f | |||
| c875a14bde | |||
| 58ba3f0665 | |||
| e9693a7cdd | |||
| a6cfc31f7a | |||
| e917b5a666 | |||
| 57d0489e1f |
@@ -0,0 +1,72 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
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.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' '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 }}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
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.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 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 }}
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
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 }}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
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 }}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
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-20.04
|
||||||
|
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 }}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
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,215 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -51,7 +51,7 @@ Following are the key capabilities of this action:
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>manifests </br></br>(Required)</td>
|
<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. Files 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, 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>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>strategy </br></br>(Required)</td>
|
<td>strategy </br></br>(Required)</td>
|
||||||
@@ -117,6 +117,10 @@ Following are the key capabilities of this action:
|
|||||||
<td>annotate-namespace</br></br>(Optional)</td>
|
<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</td>
|
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the namespace resources object or not</td>
|
||||||
</tr>
|
</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>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
## Usage Examples
|
## Usage Examples
|
||||||
@@ -124,7 +128,7 @@ Following are the key capabilities of this action:
|
|||||||
### Basic deployment (without any deployment strategy)
|
### Basic deployment (without any deployment strategy)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: Azure/k8s-deploy@v3.1
|
- uses: Azure/k8s-deploy@v4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
manifests: |
|
manifests: |
|
||||||
@@ -158,7 +162,7 @@ Following are the key capabilities of this action:
|
|||||||
### Canary deployment without service mesh
|
### Canary deployment without service mesh
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: Azure/k8s-deploy@v3.1
|
- uses: Azure/k8s-deploy@v4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||||
@@ -177,7 +181,7 @@ Following are the key capabilities of this action:
|
|||||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: Azure/k8s-deploy@v3.1
|
- uses: Azure/k8s-deploy@v4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||||
@@ -195,7 +199,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
|||||||
### Canary deployment based on Service Mesh Interface
|
### Canary deployment based on Service Mesh Interface
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: Azure/k8s-deploy@v3.1
|
- uses: Azure/k8s-deploy@v4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||||
@@ -216,7 +220,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
|||||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: Azure/k8s-deploy@v3.1
|
- uses: Azure/k8s-deploy@v4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }} '
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }} '
|
||||||
@@ -235,7 +239,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
|||||||
### Blue-Green deployment with different route methods
|
### Blue-Green deployment with different route methods
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: Azure/k8s-deploy@v3.1
|
- uses: Azure/k8s-deploy@v4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||||
@@ -255,7 +259,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
|||||||
To promote/reject the green workload created by the above snippet, the following YAML snippet could be used:
|
To promote/reject the green workload created by the above snippet, the following YAML snippet could be used:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: Azure/k8s-deploy@v3.1
|
- uses: Azure/k8s-deploy@v4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||||
@@ -312,7 +316,7 @@ jobs:
|
|||||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
secret-name: demo-k8s-secret
|
secret-name: demo-k8s-secret
|
||||||
|
|
||||||
- uses: Azure/k8s-deploy@v3.1
|
- uses: Azure/k8s-deploy@v4
|
||||||
with:
|
with:
|
||||||
action: deploy
|
action: deploy
|
||||||
manifests: |
|
manifests: |
|
||||||
@@ -358,7 +362,7 @@ jobs:
|
|||||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
secret-name: demo-k8s-secret
|
secret-name: demo-k8s-secret
|
||||||
|
|
||||||
- uses: Azure/k8s-deploy@v3.1
|
- uses: Azure/k8s-deploy@v4
|
||||||
with:
|
with:
|
||||||
action: deploy
|
action: deploy
|
||||||
manifests: |
|
manifests: |
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ inputs:
|
|||||||
namespace:
|
namespace:
|
||||||
description: 'Choose the target Kubernetes namespace. If the namespace is not provided, the commands will run in the default namespace.'
|
description: 'Choose the target Kubernetes namespace. If the namespace is not provided, the commands will run in the default namespace.'
|
||||||
required: false
|
required: false
|
||||||
|
default: default
|
||||||
manifests:
|
manifests:
|
||||||
description: 'Path to the manifest files which will be used for deployment.'
|
description: 'Path to the manifest files which will be used for deployment.'
|
||||||
required: true
|
required: true
|
||||||
@@ -72,6 +73,9 @@ inputs:
|
|||||||
name:
|
name:
|
||||||
description: 'Resource group name - Only required if using private cluster'
|
description: 'Resource group name - Only required if using private cluster'
|
||||||
required: false
|
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:
|
branding:
|
||||||
color: 'green'
|
color: 'green'
|
||||||
|
|||||||
+2
-1
@@ -6,5 +6,6 @@ module.exports = {
|
|||||||
transform: {
|
transform: {
|
||||||
'^.+\\.ts$': 'ts-jest'
|
'^.+\\.ts$': 'ts-jest'
|
||||||
},
|
},
|
||||||
verbose: true
|
verbose: true,
|
||||||
|
testTimeout: 9000
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+215
-7
@@ -9,7 +9,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.9.1",
|
"@actions/core": "^1.10.0",
|
||||||
"@actions/exec": "^1.0.0",
|
"@actions/exec": "^1.0.0",
|
||||||
"@actions/io": "^1.0.0",
|
"@actions/io": "^1.0.0",
|
||||||
"@actions/tool-cache": "1.1.2",
|
"@actions/tool-cache": "1.1.2",
|
||||||
@@ -23,15 +23,16 @@
|
|||||||
"@types/js-yaml": "^3.12.7",
|
"@types/js-yaml": "^3.12.7",
|
||||||
"@types/node": "^12.20.41",
|
"@types/node": "^12.20.41",
|
||||||
"jest": "^26.0.0",
|
"jest": "^26.0.0",
|
||||||
|
"ncc": "^0.3.6",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"ts-jest": "^26.0.0",
|
"ts-jest": "^26.0.0",
|
||||||
"typescript": "3.9.5"
|
"typescript": "3.9.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@actions/core": {
|
"node_modules/@actions/core": {
|
||||||
"version": "1.9.1",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
|
||||||
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
|
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/http-client": "^2.0.1",
|
"@actions/http-client": "^2.0.1",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
@@ -1874,6 +1875,15 @@
|
|||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/colors": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/colors/-/colors-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-qTfM2pNFeMZcLvf/RbrVAzDEVttZjFhaApfx9dplNjvHSX88Ui66zBRb/4YGob/xUWxDceirgoC1lT676asfCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.1.90"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
@@ -1968,6 +1978,15 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dateformat": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
@@ -4072,6 +4091,58 @@
|
|||||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/ncc": {
|
||||||
|
"version": "0.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/ncc/-/ncc-0.3.6.tgz",
|
||||||
|
"integrity": "sha512-OXudTB2Ebt/FnOuDoPQbaa17+tdVqSOWA+gLfPxccWwsNED1uA2zEhpoB1hwdFC9yYbio/mdV5cvOtQI3Zrx1w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"rimraf": "^2.6.1",
|
||||||
|
"tracer": "^0.8.7",
|
||||||
|
"ws": "^2.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ncc/node_modules/mkdirp": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ncc/node_modules/rimraf": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rimraf": "bin.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ncc/node_modules/safe-buffer": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-cr7dZWLwOeaFBLTIuZeYdkfO7UzGIKhjYENJFAxUOMKWGaWDm2nJM2rzxNRm5Owu0DH3ApwNo6kx5idXZfb/Iw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/ncc/node_modules/ws": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-61a+9LgtYZxTq1hAonhX8Xwpo2riK4IOR/BIVxioFbCfc3QFKmpE4x9dLExfLHKtUfVZigYa36tThVhO57erEw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.0.1",
|
||||||
|
"ultron": "~1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nice-try": {
|
"node_modules/nice-try": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||||
@@ -5812,6 +5883,15 @@
|
|||||||
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
|
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/tinytim": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinytim/-/tinytim-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-NIpsp9lBIxPNzB++HnMmUd4byzJSVbbO4F+As1Gb1IG/YQT5QvmBDjpx8SpDS8fhGC+t+Qw8ldQgbcAIaU+2cA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tmpl": {
|
"node_modules/tmpl": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
||||||
@@ -5904,6 +5984,33 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tracer": {
|
||||||
|
"version": "0.8.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/tracer/-/tracer-0.8.15.tgz",
|
||||||
|
"integrity": "sha512-ZQzlhd6zZFIpAhACiZkxLjl65XqVwi8t8UEBVGRIHAQN6nj55ftJWiFell+WSqWCP/vEycrIbUSuiyMwul+TFw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"colors": "1.2.3",
|
||||||
|
"dateformat": "3.0.3",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"tinytim": "0.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tracer/node_modules/mkdirp": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ts-jest": {
|
"node_modules/ts-jest": {
|
||||||
"version": "26.5.6",
|
"version": "26.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz",
|
||||||
@@ -6020,6 +6127,12 @@
|
|||||||
"node": ">=4.2.0"
|
"node": ">=4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ultron": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/underscore": {
|
"node_modules/underscore": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
|
||||||
@@ -6416,9 +6529,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": {
|
"@actions/core": {
|
||||||
"version": "1.9.1",
|
"version": "1.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
|
||||||
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==",
|
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@actions/http-client": "^2.0.1",
|
"@actions/http-client": "^2.0.1",
|
||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
@@ -7907,6 +8020,12 @@
|
|||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"colors": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/colors/-/colors-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-qTfM2pNFeMZcLvf/RbrVAzDEVttZjFhaApfx9dplNjvHSX88Ui66zBRb/4YGob/xUWxDceirgoC1lT676asfCQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"combined-stream": {
|
"combined-stream": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
@@ -7988,6 +8107,12 @@
|
|||||||
"whatwg-url": "^8.0.0"
|
"whatwg-url": "^8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"dateformat": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
@@ -9603,6 +9728,54 @@
|
|||||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ncc": {
|
||||||
|
"version": "0.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/ncc/-/ncc-0.3.6.tgz",
|
||||||
|
"integrity": "sha512-OXudTB2Ebt/FnOuDoPQbaa17+tdVqSOWA+gLfPxccWwsNED1uA2zEhpoB1hwdFC9yYbio/mdV5cvOtQI3Zrx1w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"rimraf": "^2.6.1",
|
||||||
|
"tracer": "^0.8.7",
|
||||||
|
"ws": "^2.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rimraf": {
|
||||||
|
"version": "2.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||||
|
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"glob": "^7.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-cr7dZWLwOeaFBLTIuZeYdkfO7UzGIKhjYENJFAxUOMKWGaWDm2nJM2rzxNRm5Owu0DH3ApwNo6kx5idXZfb/Iw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-61a+9LgtYZxTq1hAonhX8Xwpo2riK4IOR/BIVxioFbCfc3QFKmpE4x9dLExfLHKtUfVZigYa36tThVhO57erEw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "~5.0.1",
|
||||||
|
"ultron": "~1.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"nice-try": {
|
"nice-try": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||||
@@ -10966,6 +11139,12 @@
|
|||||||
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
|
"integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"tinytim": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinytim/-/tinytim-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-NIpsp9lBIxPNzB++HnMmUd4byzJSVbbO4F+As1Gb1IG/YQT5QvmBDjpx8SpDS8fhGC+t+Qw8ldQgbcAIaU+2cA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"tmpl": {
|
"tmpl": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
||||||
@@ -11039,6 +11218,29 @@
|
|||||||
"punycode": "^2.1.1"
|
"punycode": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tracer": {
|
||||||
|
"version": "0.8.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/tracer/-/tracer-0.8.15.tgz",
|
||||||
|
"integrity": "sha512-ZQzlhd6zZFIpAhACiZkxLjl65XqVwi8t8UEBVGRIHAQN6nj55ftJWiFell+WSqWCP/vEycrIbUSuiyMwul+TFw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"colors": "1.2.3",
|
||||||
|
"dateformat": "3.0.3",
|
||||||
|
"mkdirp": "^0.5.1",
|
||||||
|
"tinytim": "0.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "^1.2.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ts-jest": {
|
"ts-jest": {
|
||||||
"version": "26.5.6",
|
"version": "26.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.5.6.tgz",
|
||||||
@@ -11119,6 +11321,12 @@
|
|||||||
"integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==",
|
"integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ultron": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"underscore": {
|
"underscore": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz",
|
||||||
|
|||||||
+3
-2
@@ -4,14 +4,14 @@
|
|||||||
"author": "Deepak Sattiraju",
|
"author": "Deepak Sattiraju",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "ncc build src/run.ts -o lib",
|
"build": "npx ncc build src/run.ts -o lib",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"coverage": "jest --coverage=true",
|
"coverage": "jest --coverage=true",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format-check": "prettier --check ."
|
"format-check": "prettier --check ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.9.1",
|
"@actions/core": "^1.10.0",
|
||||||
"@actions/exec": "^1.0.0",
|
"@actions/exec": "^1.0.0",
|
||||||
"@actions/io": "^1.0.0",
|
"@actions/io": "^1.0.0",
|
||||||
"@actions/tool-cache": "1.1.2",
|
"@actions/tool-cache": "1.1.2",
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
"@types/js-yaml": "^3.12.7",
|
"@types/js-yaml": "^3.12.7",
|
||||||
"@types/node": "^12.20.41",
|
"@types/node": "^12.20.41",
|
||||||
"jest": "^26.0.0",
|
"jest": "^26.0.0",
|
||||||
|
"ncc": "^0.3.6",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"ts-jest": "^26.0.0",
|
"ts-jest": "^26.0.0",
|
||||||
"typescript": "3.9.5"
|
"typescript": "3.9.5"
|
||||||
|
|||||||
+51
-5
@@ -6,6 +6,7 @@ import {
|
|||||||
getResources,
|
getResources,
|
||||||
updateManifestFiles
|
updateManifestFiles
|
||||||
} from '../utilities/manifestUpdateUtils'
|
} from '../utilities/manifestUpdateUtils'
|
||||||
|
import {annotateAndLabelResources} from '../strategyHelpers/deploymentHelper'
|
||||||
import * as models from '../types/kubernetesTypes'
|
import * as models from '../types/kubernetesTypes'
|
||||||
import * as KubernetesManifestUtility from '../utilities/manifestStabilityUtils'
|
import * as KubernetesManifestUtility from '../utilities/manifestStabilityUtils'
|
||||||
import {
|
import {
|
||||||
@@ -15,6 +16,7 @@ import {
|
|||||||
} from '../strategyHelpers/blueGreen/blueGreenHelper'
|
} from '../strategyHelpers/blueGreen/blueGreenHelper'
|
||||||
|
|
||||||
import {BlueGreenManifests} from '../types/blueGreenTypes'
|
import {BlueGreenManifests} from '../types/blueGreenTypes'
|
||||||
|
import {DeployResult} from '../types/deployResult'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
promoteBlueGreenIngress,
|
promoteBlueGreenIngress,
|
||||||
@@ -62,6 +64,8 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
|
|||||||
const trafficSplitMethod = parseTrafficSplitMethod(
|
const trafficSplitMethod = parseTrafficSplitMethod(
|
||||||
core.getInput('traffic-split-method', {required: true})
|
core.getInput('traffic-split-method', {required: true})
|
||||||
)
|
)
|
||||||
|
let promoteResult: DeployResult
|
||||||
|
let filesToAnnotate: string[]
|
||||||
if (trafficSplitMethod == TrafficSplitMethod.SMI) {
|
if (trafficSplitMethod == TrafficSplitMethod.SMI) {
|
||||||
includeServices = true
|
includeServices = true
|
||||||
|
|
||||||
@@ -77,26 +81,35 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
|
|||||||
core.startGroup(
|
core.startGroup(
|
||||||
'Deploying input manifests with SMI canary strategy from promote'
|
'Deploying input manifests with SMI canary strategy from promote'
|
||||||
)
|
)
|
||||||
await SMICanaryDeploymentHelper.deploySMICanary(
|
|
||||||
|
promoteResult = await SMICanaryDeploymentHelper.deploySMICanary(
|
||||||
manifestFilesForDeployment,
|
manifestFilesForDeployment,
|
||||||
kubectl,
|
kubectl,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
core.startGroup('Redirecting traffic to stable deployment')
|
core.startGroup('Redirecting traffic to stable deployment')
|
||||||
await SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(
|
const stableRedirectManifests =
|
||||||
kubectl,
|
await SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(
|
||||||
manifests
|
kubectl,
|
||||||
|
manifests
|
||||||
|
)
|
||||||
|
|
||||||
|
filesToAnnotate = promoteResult.manifestFiles.concat(
|
||||||
|
stableRedirectManifests
|
||||||
)
|
)
|
||||||
|
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
} else {
|
} else {
|
||||||
core.startGroup('Deploying input manifests from promote')
|
core.startGroup('Deploying input manifests from promote')
|
||||||
await PodCanaryHelper.deployPodCanary(
|
promoteResult = await PodCanaryHelper.deployPodCanary(
|
||||||
manifestFilesForDeployment,
|
manifestFilesForDeployment,
|
||||||
kubectl,
|
kubectl,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
filesToAnnotate = promoteResult.manifestFiles
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +126,23 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
core.endGroup()
|
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}`)
|
||||||
|
}
|
||||||
|
const resources: Resource[] = getResources(
|
||||||
|
filesToAnnotate,
|
||||||
|
models.DEPLOYMENT_TYPES.concat([
|
||||||
|
models.DiscoveryAndLoadBalancerResource.SERVICE
|
||||||
|
])
|
||||||
|
)
|
||||||
|
await annotateAndLabelResources(filesToAnnotate, kubectl, resources, allPods)
|
||||||
|
core.endGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function promoteBlueGreen(kubectl: Kubectl, manifests: string[]) {
|
async function promoteBlueGreen(kubectl: Kubectl, manifests: string[]) {
|
||||||
@@ -186,4 +216,20 @@ async function promoteBlueGreen(kubectl: Kubectl, manifests: string[]) {
|
|||||||
await deleteGreenObjects(kubectl, manifestObjects.deploymentEntityList)
|
await deleteGreenObjects(kubectl, manifestObjects.deploymentEntityList)
|
||||||
}
|
}
|
||||||
core.endGroup()
|
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,
|
||||||
|
resources,
|
||||||
|
allPods
|
||||||
|
)
|
||||||
|
core.endGroup()
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -5,7 +5,7 @@ import {promote} from './actions/promote'
|
|||||||
import {reject} from './actions/reject'
|
import {reject} from './actions/reject'
|
||||||
import {Action, parseAction} from './types/action'
|
import {Action, parseAction} from './types/action'
|
||||||
import {parseDeploymentStrategy} from './types/deploymentStrategy'
|
import {parseDeploymentStrategy} from './types/deploymentStrategy'
|
||||||
import {getFilesFromDirectories} from './utilities/fileUtils'
|
import {getFilesFromDirectoriesAndURLs} from './utilities/fileUtils'
|
||||||
import {PrivateKubectl} from './types/privatekubectl'
|
import {PrivateKubectl} from './types/privatekubectl'
|
||||||
|
|
||||||
export async function run() {
|
export async function run() {
|
||||||
@@ -26,23 +26,26 @@ export async function run() {
|
|||||||
.map((manifest) => manifest.trim()) // remove surrounding whitespace
|
.map((manifest) => manifest.trim()) // remove surrounding whitespace
|
||||||
.filter((manifest) => manifest.length > 0) // remove any blanks
|
.filter((manifest) => manifest.length > 0) // remove any blanks
|
||||||
|
|
||||||
const fullManifestFilePaths = getFilesFromDirectories(manifestFilePaths)
|
const fullManifestFilePaths = await getFilesFromDirectoriesAndURLs(
|
||||||
|
manifestFilePaths
|
||||||
|
)
|
||||||
const kubectlPath = await getKubectlPath()
|
const kubectlPath = await getKubectlPath()
|
||||||
const namespace = core.getInput('namespace') || 'default'
|
const namespace = core.getInput('namespace') || 'default'
|
||||||
const isPrivateCluster =
|
const isPrivateCluster =
|
||||||
core.getInput('private-cluster').toLowerCase() === 'true'
|
core.getInput('private-cluster').toLowerCase() === 'true'
|
||||||
const resourceGroup = core.getInput('resource-group') || ''
|
const resourceGroup = core.getInput('resource-group') || ''
|
||||||
const resourceName = core.getInput('name') || ''
|
const resourceName = core.getInput('name') || ''
|
||||||
|
const skipTlsVerify = core.getBooleanInput('skip-tls-verify')
|
||||||
|
|
||||||
const kubectl = isPrivateCluster
|
const kubectl = isPrivateCluster
|
||||||
? new PrivateKubectl(
|
? new PrivateKubectl(
|
||||||
kubectlPath,
|
kubectlPath,
|
||||||
namespace,
|
namespace,
|
||||||
true,
|
skipTlsVerify,
|
||||||
resourceGroup,
|
resourceGroup,
|
||||||
resourceName
|
resourceName
|
||||||
)
|
)
|
||||||
: new Kubectl(kubectlPath, namespace, true)
|
: new Kubectl(kubectlPath, namespace, skipTlsVerify)
|
||||||
|
|
||||||
// run action
|
// run action
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ describe('deploy tests', () => {
|
|||||||
RouteStrategy.SMI
|
RouteStrategy.SMI
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(smiResult.objects.length).toBe(3)
|
expect(smiResult.objects.length).toBe(6)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('correctly deploys blue/green ingress', async () => {
|
test('correctly deploys blue/green ingress', async () => {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
import {setupSMI} from './smiBlueGreenHelper'
|
import {setupSMI} from './smiBlueGreenHelper'
|
||||||
|
|
||||||
import {routeBlueGreenForDeploy} from './route'
|
import {routeBlueGreenForDeploy} from './route'
|
||||||
|
import {DeployResult} from '../../types/deployResult'
|
||||||
|
|
||||||
export async function deployBlueGreen(
|
export async function deployBlueGreen(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
@@ -35,9 +36,17 @@ export async function deployBlueGreen(
|
|||||||
})()
|
})()
|
||||||
|
|
||||||
core.startGroup('Routing blue green')
|
core.startGroup('Routing blue green')
|
||||||
await routeBlueGreenForDeploy(kubectl, files, routeStrategy)
|
const routeDeployment = await routeBlueGreenForDeploy(
|
||||||
|
kubectl,
|
||||||
|
files,
|
||||||
|
routeStrategy
|
||||||
|
)
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
|
|
||||||
|
blueGreenDeployment.objects.push(...routeDeployment.objects)
|
||||||
|
blueGreenDeployment.deployResult.manifestFiles.push(
|
||||||
|
...routeDeployment.deployResult.manifestFiles
|
||||||
|
)
|
||||||
return blueGreenDeployment
|
return blueGreenDeployment
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,10 +65,16 @@ export async function deployBlueGreenSMI(
|
|||||||
manifestObjects.unroutedServiceEntityList
|
manifestObjects.unroutedServiceEntityList
|
||||||
)
|
)
|
||||||
|
|
||||||
await deployObjects(kubectl, newObjectsList)
|
const otherObjDeployment: DeployResult = await deployObjects(
|
||||||
|
kubectl,
|
||||||
|
newObjectsList
|
||||||
|
)
|
||||||
|
|
||||||
// make extraservices and trafficsplit
|
// make extraservices and trafficsplit
|
||||||
await setupSMI(kubectl, manifestObjects.serviceEntityList)
|
const smiAndSvcDeployment = await setupSMI(
|
||||||
|
kubectl,
|
||||||
|
manifestObjects.serviceEntityList
|
||||||
|
)
|
||||||
|
|
||||||
// create new deloyments
|
// create new deloyments
|
||||||
const blueGreenDeployment: BlueGreenDeployment = await deployWithLabel(
|
const blueGreenDeployment: BlueGreenDeployment = await deployWithLabel(
|
||||||
@@ -67,10 +82,18 @@ export async function deployBlueGreenSMI(
|
|||||||
manifestObjects.deploymentEntityList,
|
manifestObjects.deploymentEntityList,
|
||||||
GREEN_LABEL_VALUE
|
GREEN_LABEL_VALUE
|
||||||
)
|
)
|
||||||
return {
|
|
||||||
deployResult: blueGreenDeployment.deployResult,
|
blueGreenDeployment.objects.push(...newObjectsList)
|
||||||
objects: [].concat(blueGreenDeployment.objects, newObjectsList)
|
blueGreenDeployment.objects.push(...smiAndSvcDeployment.objects)
|
||||||
}
|
|
||||||
|
blueGreenDeployment.deployResult.manifestFiles.push(
|
||||||
|
...otherObjDeployment.manifestFiles
|
||||||
|
)
|
||||||
|
blueGreenDeployment.deployResult.manifestFiles.push(
|
||||||
|
...smiAndSvcDeployment.deployResult.manifestFiles
|
||||||
|
)
|
||||||
|
|
||||||
|
return blueGreenDeployment
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deployBlueGreenIngress(
|
export async function deployBlueGreenIngress(
|
||||||
|
|||||||
@@ -61,6 +61,6 @@ describe('reject tests', () => {
|
|||||||
.spyOn(TSutils, 'getTrafficSplitAPIVersion')
|
.spyOn(TSutils, 'getTrafficSplitAPIVersion')
|
||||||
.mockImplementation(() => Promise.resolve('v1alpha3'))
|
.mockImplementation(() => Promise.resolve('v1alpha3'))
|
||||||
const rejectResult = await rejectBlueGreenSMI(kubectl, testObjects)
|
const rejectResult = await rejectBlueGreenSMI(kubectl, testObjects)
|
||||||
expect(rejectResult.deleteResult).toHaveLength(4)
|
expect(rejectResult.deleteResult).toHaveLength(2)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -193,11 +193,8 @@ describe('SMI Helper tests', () => {
|
|||||||
|
|
||||||
test('cleanupSMI test', async () => {
|
test('cleanupSMI test', async () => {
|
||||||
const deleteObjects = await cleanupSMI(kc, testObjects.serviceEntityList)
|
const deleteObjects = await cleanupSMI(kc, testObjects.serviceEntityList)
|
||||||
expect(deleteObjects).toHaveLength(3)
|
expect(deleteObjects).toHaveLength(1)
|
||||||
expect(deleteObjects[0].name).toBe('nginx-service-trafficsplit')
|
expect(deleteObjects[0].name).toBe('nginx-service-green')
|
||||||
expect(deleteObjects[1].name).toBe('nginx-service-green')
|
expect(deleteObjects[0].kind).toBe('Service')
|
||||||
expect(deleteObjects[1].kind).toBe('Service')
|
|
||||||
expect(deleteObjects[2].name).toBe('nginx-service-stable')
|
|
||||||
expect(deleteObjects[2].kind).toBe('Service')
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -178,14 +178,6 @@ export async function cleanupSMI(
|
|||||||
const deleteList: K8sDeleteObject[] = []
|
const deleteList: K8sDeleteObject[] = []
|
||||||
|
|
||||||
serviceEntityList.forEach((serviceObject) => {
|
serviceEntityList.forEach((serviceObject) => {
|
||||||
deleteList.push({
|
|
||||||
name: getBlueGreenResourceName(
|
|
||||||
serviceObject.metadata.name,
|
|
||||||
TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX
|
|
||||||
),
|
|
||||||
kind: TRAFFIC_SPLIT_OBJECT
|
|
||||||
})
|
|
||||||
|
|
||||||
deleteList.push({
|
deleteList.push({
|
||||||
name: getBlueGreenResourceName(
|
name: getBlueGreenResourceName(
|
||||||
serviceObject.metadata.name,
|
serviceObject.metadata.name,
|
||||||
@@ -193,14 +185,6 @@ export async function cleanupSMI(
|
|||||||
),
|
),
|
||||||
kind: serviceObject.kind
|
kind: serviceObject.kind
|
||||||
})
|
})
|
||||||
|
|
||||||
deleteList.push({
|
|
||||||
name: getBlueGreenResourceName(
|
|
||||||
serviceObject.metadata.name,
|
|
||||||
STABLE_SUFFIX
|
|
||||||
),
|
|
||||||
kind: serviceObject.kind
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// delete all objects
|
// delete all objects
|
||||||
|
|||||||
@@ -29,12 +29,17 @@ export async function deleteCanaryDeployment(
|
|||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
manifestFilePaths: string[],
|
manifestFilePaths: string[],
|
||||||
includeServices: boolean
|
includeServices: boolean
|
||||||
) {
|
): Promise<string[]> {
|
||||||
if (manifestFilePaths == null || manifestFilePaths.length == 0) {
|
if (manifestFilePaths == null || manifestFilePaths.length == 0) {
|
||||||
throw new Error('Manifest files for deleting canary deployment not found')
|
throw new Error('Manifest files for deleting canary deployment not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
await cleanUpCanary(kubectl, manifestFilePaths, includeServices)
|
const deletedFiles = await cleanUpCanary(
|
||||||
|
kubectl,
|
||||||
|
manifestFilePaths,
|
||||||
|
includeServices
|
||||||
|
)
|
||||||
|
return deletedFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
export function markResourceAsStable(inputObject: any): object {
|
export function markResourceAsStable(inputObject: any): object {
|
||||||
@@ -189,7 +194,7 @@ async function cleanUpCanary(
|
|||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
files: string[],
|
files: string[],
|
||||||
includeServices: boolean
|
includeServices: boolean
|
||||||
) {
|
): Promise<string[]> {
|
||||||
const deleteObject = async function (kind, name) {
|
const deleteObject = async function (kind, name) {
|
||||||
try {
|
try {
|
||||||
const result = await kubectl.delete([kind, name])
|
const result = await kubectl.delete([kind, name])
|
||||||
@@ -199,6 +204,8 @@ async function cleanUpCanary(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deletedFiles: string[] = []
|
||||||
|
|
||||||
for (const filePath of files) {
|
for (const filePath of files) {
|
||||||
const fileContents = fs.readFileSync(filePath).toString()
|
const fileContents = fs.readFileSync(filePath).toString()
|
||||||
|
|
||||||
@@ -211,6 +218,7 @@ async function cleanUpCanary(
|
|||||||
isDeploymentEntity(kind) ||
|
isDeploymentEntity(kind) ||
|
||||||
(includeServices && isServiceEntity(kind))
|
(includeServices && isServiceEntity(kind))
|
||||||
) {
|
) {
|
||||||
|
deletedFiles.push(filePath)
|
||||||
const canaryObjectName = getCanaryResourceName(name)
|
const canaryObjectName = getCanaryResourceName(name)
|
||||||
const baselineObjectName = getBaselineResourceName(name)
|
const baselineObjectName = getBaselineResourceName(name)
|
||||||
|
|
||||||
@@ -219,4 +227,6 @@ async function cleanUpCanary(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return deletedFiles
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import * as fileHelper from '../../utilities/fileUtils'
|
|||||||
import * as canaryDeploymentHelper from './canaryHelper'
|
import * as canaryDeploymentHelper from './canaryHelper'
|
||||||
import {isDeploymentEntity} from '../../types/kubernetesTypes'
|
import {isDeploymentEntity} from '../../types/kubernetesTypes'
|
||||||
import {getReplicaCount} from '../../utilities/manifestUpdateUtils'
|
import {getReplicaCount} from '../../utilities/manifestUpdateUtils'
|
||||||
|
import {DeployResult} from '../../types/deployResult'
|
||||||
|
|
||||||
export async function deployPodCanary(
|
export async function deployPodCanary(
|
||||||
filePaths: string[],
|
filePaths: string[],
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
onlyDeployStable: boolean = false
|
onlyDeployStable: boolean = false
|
||||||
) {
|
): Promise<DeployResult> {
|
||||||
const newObjectsList = []
|
const newObjectsList = []
|
||||||
const percentage = parseInt(core.getInput('percentage', {required: true}))
|
const percentage = parseInt(core.getInput('percentage', {required: true}))
|
||||||
|
|
||||||
@@ -71,8 +72,8 @@ export async function deployPodCanary(
|
|||||||
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
||||||
const forceDeployment = core.getInput('force').toLowerCase() === 'true'
|
const forceDeployment = core.getInput('force').toLowerCase() === 'true'
|
||||||
|
|
||||||
const result = await kubectl.apply(manifestFiles, forceDeployment)
|
const execResult = await kubectl.apply(manifestFiles, forceDeployment)
|
||||||
return {result, newFilePaths: manifestFiles}
|
return {execResult, manifestFiles}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calculateReplicaCountForCanary(
|
export function calculateReplicaCountForCanary(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import * as podCanaryHelper from './podCanaryHelper'
|
|||||||
import {isDeploymentEntity, isServiceEntity} from '../../types/kubernetesTypes'
|
import {isDeploymentEntity, isServiceEntity} from '../../types/kubernetesTypes'
|
||||||
import {checkForErrors} from '../../utilities/kubectlUtils'
|
import {checkForErrors} from '../../utilities/kubectlUtils'
|
||||||
import {inputAnnotations} from '../../inputUtils'
|
import {inputAnnotations} from '../../inputUtils'
|
||||||
|
import {DeployResult} from '../../types/deployResult'
|
||||||
|
|
||||||
const TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX = '-workflow-rollout'
|
const TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX = '-workflow-rollout'
|
||||||
const TRAFFIC_SPLIT_OBJECT = 'TrafficSplit'
|
const TRAFFIC_SPLIT_OBJECT = 'TrafficSplit'
|
||||||
@@ -18,7 +19,7 @@ export async function deploySMICanary(
|
|||||||
filePaths: string[],
|
filePaths: string[],
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
onlyDeployStable: boolean = false
|
onlyDeployStable: boolean = false
|
||||||
) {
|
): Promise<DeployResult> {
|
||||||
const canaryReplicasInput = core.getInput('baseline-and-canary-replicas')
|
const canaryReplicasInput = core.getInput('baseline-and-canary-replicas')
|
||||||
let canaryReplicaCount
|
let canaryReplicaCount
|
||||||
let calculateReplicas = true
|
let calculateReplicas = true
|
||||||
@@ -97,11 +98,15 @@ export async function deploySMICanary(
|
|||||||
const newFilePaths = fileHelper.writeObjectsToFile(newObjectsList)
|
const newFilePaths = fileHelper.writeObjectsToFile(newObjectsList)
|
||||||
const forceDeployment = core.getInput('force').toLowerCase() === 'true'
|
const forceDeployment = core.getInput('force').toLowerCase() === 'true'
|
||||||
const result = await kubectl.apply(newFilePaths, forceDeployment)
|
const result = await kubectl.apply(newFilePaths, forceDeployment)
|
||||||
await createCanaryService(kubectl, filePaths)
|
const svcDeploymentFiles = await createCanaryService(kubectl, filePaths)
|
||||||
return {result, newFilePaths}
|
newFilePaths.push(...svcDeploymentFiles)
|
||||||
|
return {execResult: result, manifestFiles: newFilePaths}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createCanaryService(kubectl: Kubectl, filePaths: string[]) {
|
async function createCanaryService(
|
||||||
|
kubectl: Kubectl,
|
||||||
|
filePaths: string[]
|
||||||
|
): Promise<string[]> {
|
||||||
const newObjectsList = []
|
const newObjectsList = []
|
||||||
const trafficObjectsList: string[] = []
|
const trafficObjectsList: string[] = []
|
||||||
|
|
||||||
@@ -190,6 +195,7 @@ async function createCanaryService(kubectl: Kubectl, filePaths: string[]) {
|
|||||||
|
|
||||||
const result = await kubectl.apply(manifestFiles, forceDeployment)
|
const result = await kubectl.apply(manifestFiles, forceDeployment)
|
||||||
checkForErrors([result])
|
checkForErrors([result])
|
||||||
|
return manifestFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function redirectTrafficToCanaryDeployment(
|
export async function redirectTrafficToCanaryDeployment(
|
||||||
@@ -202,8 +208,8 @@ export async function redirectTrafficToCanaryDeployment(
|
|||||||
export async function redirectTrafficToStableDeployment(
|
export async function redirectTrafficToStableDeployment(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
manifestFilePaths: string[]
|
manifestFilePaths: string[]
|
||||||
) {
|
): Promise<string[]> {
|
||||||
await adjustTraffic(kubectl, manifestFilePaths, 1000, 0)
|
return await adjustTraffic(kubectl, manifestFilePaths, 1000, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function adjustTraffic(
|
async function adjustTraffic(
|
||||||
@@ -245,6 +251,7 @@ async function adjustTraffic(
|
|||||||
const forceDeployment = core.getInput('force').toLowerCase() === 'true'
|
const forceDeployment = core.getInput('force').toLowerCase() === 'true'
|
||||||
const result = await kubectl.apply(trafficSplitManifests, forceDeployment)
|
const result = await kubectl.apply(trafficSplitManifests, forceDeployment)
|
||||||
checkForErrors([result])
|
checkForErrors([result])
|
||||||
|
return trafficSplitManifests
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateTrafficSplitObject(
|
async function updateTrafficSplitObject(
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ import {
|
|||||||
normalizeWorkflowStrLabel
|
normalizeWorkflowStrLabel
|
||||||
} from '../utilities/githubUtils'
|
} from '../utilities/githubUtils'
|
||||||
import {getDeploymentConfig} from '../utilities/dockerUtils'
|
import {getDeploymentConfig} from '../utilities/dockerUtils'
|
||||||
|
import {deploy} from '../actions/deploy'
|
||||||
|
import {DeployResult} from '../types/deployResult'
|
||||||
|
|
||||||
export async function deployManifests(
|
export async function deployManifests(
|
||||||
files: string[],
|
files: string[],
|
||||||
@@ -48,13 +50,13 @@ export async function deployManifests(
|
|||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
switch (deploymentStrategy) {
|
switch (deploymentStrategy) {
|
||||||
case DeploymentStrategy.CANARY: {
|
case DeploymentStrategy.CANARY: {
|
||||||
const {result, newFilePaths} =
|
const canaryDeployResult: DeployResult =
|
||||||
trafficSplitMethod == TrafficSplitMethod.SMI
|
trafficSplitMethod == TrafficSplitMethod.SMI
|
||||||
? await deploySMICanary(files, kubectl)
|
? await deploySMICanary(files, kubectl)
|
||||||
: await deployPodCanary(files, kubectl)
|
: await deployPodCanary(files, kubectl)
|
||||||
|
|
||||||
checkForErrors([result])
|
checkForErrors([canaryDeployResult.execResult])
|
||||||
return newFilePaths
|
return canaryDeployResult.manifestFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
case DeploymentStrategy.BLUE_GREEN: {
|
case DeploymentStrategy.BLUE_GREEN: {
|
||||||
@@ -73,7 +75,12 @@ export async function deployManifests(
|
|||||||
)
|
)
|
||||||
|
|
||||||
checkForErrors([blueGreenDeployment.deployResult.execResult])
|
checkForErrors([blueGreenDeployment.deployResult.execResult])
|
||||||
return blueGreenDeployment.deployResult.manifestFiles
|
const deployedManifestFiles =
|
||||||
|
blueGreenDeployment.deployResult.manifestFiles
|
||||||
|
core.debug(
|
||||||
|
`from blue-green service, deployed manifest files are ${deployedManifestFiles}`
|
||||||
|
)
|
||||||
|
return deployedManifestFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
case DeploymentStrategy.BASIC: {
|
case DeploymentStrategy.BASIC: {
|
||||||
@@ -178,6 +185,18 @@ async function annotateResources(
|
|||||||
annotationKey
|
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(
|
const annotationKeyValStr = `${annotationKey}=${getWorkflowAnnotations(
|
||||||
lastSuccessSha,
|
lastSuccessSha,
|
||||||
workflowFilePath,
|
workflowFilePath,
|
||||||
@@ -192,7 +211,17 @@ async function annotateResources(
|
|||||||
await kubectl.annotate('namespace', namespace, annotationKeyValStr)
|
await kubectl.annotate('namespace', namespace, annotationKeyValStr)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
annotateResults.push(await kubectl.annotateFiles(files, annotationKeyValStr))
|
for (const file of files) {
|
||||||
|
try {
|
||||||
|
const annotateResult = await kubectl.annotateFiles(
|
||||||
|
file,
|
||||||
|
annotationKeyValStr
|
||||||
|
)
|
||||||
|
annotateResults.push(annotateResult)
|
||||||
|
} catch (e) {
|
||||||
|
core.warning(`failed to annotate resource: ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const resource of resourceTypes) {
|
for (const resource of resourceTypes) {
|
||||||
if (
|
if (
|
||||||
@@ -226,5 +255,14 @@ async function labelResources(
|
|||||||
`workflow=${cleanLabel(label)}`
|
`workflow=${cleanLabel(label)}`
|
||||||
]
|
]
|
||||||
|
|
||||||
checkForErrors([await kubectl.labelFiles(files, labels)], true)
|
const labelResults = []
|
||||||
|
for (const file of files) {
|
||||||
|
try {
|
||||||
|
const labelResult = await kubectl.labelFiles(files, labels)
|
||||||
|
labelResults.push(labelResult)
|
||||||
|
} catch (e) {
|
||||||
|
core.warning(`failed to annotate resource: ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkForErrors(labelResults, true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
+17
-11
@@ -48,17 +48,6 @@ describe('Kubectl class', () => {
|
|||||||
return execReturn
|
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', () => {
|
describe('with a success exec return in testNamespace', () => {
|
||||||
@@ -364,4 +353,21 @@ describe('Kubectl class', () => {
|
|||||||
const result = await kubectl.getNewReplicaSet(deployment)
|
const result = await kubectl.getNewReplicaSet(deployment)
|
||||||
expect(result).toBe(name)
|
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}
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
+19
-7
@@ -102,13 +102,18 @@ export class Kubectl {
|
|||||||
files: string | string[],
|
files: string | string[],
|
||||||
annotation: string
|
annotation: string
|
||||||
): Promise<ExecOutput> {
|
): Promise<ExecOutput> {
|
||||||
|
const filesToAnnotate = createInlineArray(files)
|
||||||
|
core.debug(`annotating ${filesToAnnotate} with annotation ${annotation}`)
|
||||||
const args = [
|
const args = [
|
||||||
'annotate',
|
'annotate',
|
||||||
'-f',
|
'-f',
|
||||||
createInlineArray(files),
|
filesToAnnotate,
|
||||||
annotation,
|
annotation,
|
||||||
'--overwrite'
|
'--overwrite'
|
||||||
]
|
]
|
||||||
|
core.debug(
|
||||||
|
`sending args from annotate to execute: ${JSON.stringify(args)}`
|
||||||
|
)
|
||||||
return await this.execute(args)
|
return await this.execute(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,18 +171,25 @@ export class Kubectl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async execute(args: string[], silent: boolean = false) {
|
protected async execute(args: string[], silent: boolean = false) {
|
||||||
if (this.ignoreSSLErrors) {
|
args = args.concat(this.getExecuteFlags())
|
||||||
args.push('--insecure-skip-tls-verify')
|
|
||||||
}
|
|
||||||
if (this.namespace && this.namespace != 'default') {
|
|
||||||
args = args.concat(['--namespace', this.namespace])
|
|
||||||
}
|
|
||||||
core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`)
|
core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`)
|
||||||
|
|
||||||
return await getExecOutput(this.kubectlPath, args, {
|
return await getExecOutput(this.kubectlPath, args, {
|
||||||
silent
|
silent
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getExecuteFlags(): string[] {
|
||||||
|
const flags = []
|
||||||
|
if (this.ignoreSSLErrors) {
|
||||||
|
flags.push('--insecure-skip-tls-verify')
|
||||||
|
}
|
||||||
|
if (this.namespace) {
|
||||||
|
flags.push('--namespace', this.namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
return flags
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getKubectlPath() {
|
export async function getKubectlPath() {
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import {PrivateKubectl} from './privatekubectl'
|
||||||
|
|
||||||
|
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('')
|
||||||
|
|
||||||
|
it('should extract filenames correctly', () => {
|
||||||
|
expect(mockKube.extractFilesnames(testString)).toEqual(
|
||||||
|
'test.yml test2.yml test3.yml test4.yml test5.yml'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
+58
-15
@@ -1,4 +1,5 @@
|
|||||||
import {Kubectl} from './kubectl'
|
import {Kubectl} from './kubectl'
|
||||||
|
import * as minimist from 'minimist'
|
||||||
import {ExecOptions, ExecOutput, getExecOutput} from '@actions/exec'
|
import {ExecOptions, ExecOutput, getExecOutput} from '@actions/exec'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
@@ -7,10 +8,16 @@ import * as path from 'path'
|
|||||||
|
|
||||||
export class PrivateKubectl extends Kubectl {
|
export class PrivateKubectl extends Kubectl {
|
||||||
protected async execute(args: string[], silent: boolean = false) {
|
protected async execute(args: string[], silent: boolean = false) {
|
||||||
|
args = args.concat(this.getExecuteFlags())
|
||||||
|
|
||||||
args.unshift('kubectl')
|
args.unshift('kubectl')
|
||||||
let kubectlCmd = args.join(' ')
|
let kubectlCmd = args.join(' ')
|
||||||
let addFileFlag = false
|
let addFileFlag = false
|
||||||
let eo = <ExecOptions>{silent}
|
let eo = <ExecOptions>{
|
||||||
|
silent: true,
|
||||||
|
failOnStdErr: false,
|
||||||
|
ignoreReturnCode: true
|
||||||
|
}
|
||||||
|
|
||||||
if (this.containsFilenames(kubectlCmd)) {
|
if (this.containsFilenames(kubectlCmd)) {
|
||||||
// For private clusters, files will referenced solely by their basename
|
// For private clusters, files will referenced solely by their basename
|
||||||
@@ -18,6 +25,13 @@ export class PrivateKubectl extends Kubectl {
|
|||||||
addFileFlag = true
|
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 = [
|
const privateClusterArgs = [
|
||||||
'aks',
|
'aks',
|
||||||
'command',
|
'command',
|
||||||
@@ -27,7 +41,7 @@ export class PrivateKubectl extends Kubectl {
|
|||||||
'--name',
|
'--name',
|
||||||
this.name,
|
this.name,
|
||||||
'--command',
|
'--command',
|
||||||
kubectlCmd
|
`${kubectlCmd}`
|
||||||
]
|
]
|
||||||
|
|
||||||
if (addFileFlag) {
|
if (addFileFlag) {
|
||||||
@@ -52,7 +66,28 @@ export class PrivateKubectl extends Kubectl {
|
|||||||
core.debug(
|
core.debug(
|
||||||
`private cluster Kubectl run with invoke command: ${kubectlCmd}`
|
`private cluster Kubectl run with invoke command: ${kubectlCmd}`
|
||||||
)
|
)
|
||||||
return await getExecOutput('az', privateClusterArgs, eo)
|
|
||||||
|
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
|
||||||
|
)}`
|
||||||
|
)
|
||||||
|
const runObj: {logs: string; exitCode: number} = JSON.parse(
|
||||||
|
runOutput.stdout
|
||||||
|
)
|
||||||
|
if (!silent) core.info(runObj.logs)
|
||||||
|
if (runOutput.exitCode !== 0 && runObj.exitCode !== 0) {
|
||||||
|
throw Error(`failed private cluster Kubectl command: ${kubectlCmd}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
exitCode: runObj.exitCode,
|
||||||
|
stdout: runObj.logs,
|
||||||
|
stderr: ''
|
||||||
|
} as ExecOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
private replaceFilnamesWithBasenames(kubectlCmd: string) {
|
private replaceFilnamesWithBasenames(kubectlCmd: string) {
|
||||||
@@ -71,23 +106,31 @@ export class PrivateKubectl extends Kubectl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public extractFilesnames(strToParse: string) {
|
public extractFilesnames(strToParse: string) {
|
||||||
let start = strToParse.indexOf('-filename')
|
const fileNames: string[] = []
|
||||||
let offset = 7
|
const argv = minimist(strToParse.split(' '))
|
||||||
|
const fArg = 'f'
|
||||||
|
const filenameArg = 'filename'
|
||||||
|
|
||||||
if (start == -1) {
|
fileNames.push(...this.extractFilesFromMinimist(argv, fArg))
|
||||||
start = strToParse.indexOf('-f')
|
fileNames.push(...this.extractFilesFromMinimist(argv, filenameArg))
|
||||||
|
|
||||||
if (start == -1) {
|
return fileNames.join(' ')
|
||||||
return ''
|
}
|
||||||
|
|
||||||
|
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(','))
|
||||||
}
|
}
|
||||||
offset = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let temp = strToParse.substring(start + offset)
|
return toReturn
|
||||||
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) {
|
private containsFilenames(str: string) {
|
||||||
|
|||||||
@@ -1,11 +1,45 @@
|
|||||||
import {getFilesFromDirectories} from './fileUtils'
|
import {
|
||||||
|
getFilesFromDirectoriesAndURLs,
|
||||||
|
getTempDirectory,
|
||||||
|
urlFileKind,
|
||||||
|
writeYamlFromURLToFile
|
||||||
|
} from './fileUtils'
|
||||||
|
|
||||||
|
import * as yaml from 'js-yaml'
|
||||||
|
import * as fs from 'fs'
|
||||||
import * as path from 'path'
|
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', () => {
|
describe('File utils', () => {
|
||||||
it('detects files in nested directories and ignores non-manifest files and empty dirs', () => {
|
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'
|
||||||
|
|
||||||
const testPath = path.join('test', 'unit', 'manifests')
|
const testPath = path.join('test', 'unit', 'manifests')
|
||||||
const testSearch: string[] = getFilesFromDirectories([testPath])
|
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 expectedManifests = [
|
const expectedManifests = [
|
||||||
'test/unit/manifests/manifest_test_dir/another_layer/deep-ingress.yaml',
|
'test/unit/manifests/manifest_test_dir/another_layer/deep-ingress.yaml',
|
||||||
@@ -17,13 +51,18 @@ describe('File utils', () => {
|
|||||||
]
|
]
|
||||||
|
|
||||||
// is there a more efficient way to test equality w random order?
|
// is there a more efficient way to test equality w random order?
|
||||||
expect(testSearch).toHaveLength(7)
|
expect(testSearch).toHaveLength(8)
|
||||||
expectedManifests.forEach((fileName) => {
|
expectedManifests.forEach((fileName) => {
|
||||||
expect(testSearch).toContain(fileName)
|
if (fileName.startsWith('test/unit')) {
|
||||||
|
expect(testSearch).toContain(fileName)
|
||||||
|
} else {
|
||||||
|
expect(fileName.includes(urlFileKind)).toBe(true)
|
||||||
|
expect(fileName.startsWith(getTempDirectory()))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('crashes when an invalid file is provided', () => {
|
it('crashes when an invalid file is provided', async () => {
|
||||||
const badPath = path.join('test', 'unit', 'manifests', 'nonexistent.yaml')
|
const badPath = path.join('test', 'unit', 'manifests', 'nonexistent.yaml')
|
||||||
const goodPath = path.join(
|
const goodPath = path.join(
|
||||||
'test',
|
'test',
|
||||||
@@ -32,12 +71,12 @@ describe('File utils', () => {
|
|||||||
'manifest_test_dir'
|
'manifest_test_dir'
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(() => {
|
expect(
|
||||||
getFilesFromDirectories([badPath, goodPath])
|
getFilesFromDirectoriesAndURLs([badPath, goodPath])
|
||||||
}).toThrowError()
|
).rejects.toThrowError()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("doesn't duplicate files when nested dir included", () => {
|
it("doesn't duplicate files when nested dir included", async () => {
|
||||||
const outerPath = path.join('test', 'unit', 'manifests')
|
const outerPath = path.join('test', 'unit', 'manifests')
|
||||||
const fileAtOuter = path.join(
|
const fileAtOuter = path.join(
|
||||||
'test',
|
'test',
|
||||||
@@ -53,11 +92,16 @@ describe('File utils', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
getFilesFromDirectories([outerPath, fileAtOuter, innerPath])
|
await getFilesFromDirectoriesAndURLs([
|
||||||
|
outerPath,
|
||||||
|
fileAtOuter,
|
||||||
|
innerPath
|
||||||
|
])
|
||||||
).toHaveLength(7)
|
).toHaveLength(7)
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
// files that don't exist / nested files that don't exist / something else with non-manifest
|
it('throws an error for an invalid URL', async () => {
|
||||||
// lots of combinations of pointing to a directory and non yaml/yaml file
|
const badUrl = 'https://www.github.com'
|
||||||
// similarly named files in different folders
|
await expect(writeYamlFromURLToFile(badUrl, 0)).rejects.toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
+104
-5
@@ -1,8 +1,15 @@
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
|
import * as https from 'https'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
|
import * as yaml from 'js-yaml'
|
||||||
|
import {Errorable, succeeded, failed, Failed} from '../types/errorable'
|
||||||
import {getCurrentTime} from './timeUtils'
|
import {getCurrentTime} from './timeUtils'
|
||||||
|
import {isHttpUrl} from './githubUtils'
|
||||||
|
import {K8sObject} from '../types/k8sObject'
|
||||||
|
|
||||||
|
export const urlFileKind = 'urlfile'
|
||||||
|
|
||||||
export function getTempDirectory(): string {
|
export function getTempDirectory(): string {
|
||||||
return process.env['runner.tempDirectory'] || os.tmpdir()
|
return process.env['runner.tempDirectory'] || os.tmpdir()
|
||||||
@@ -62,12 +69,27 @@ function getManifestFileName(kind: string, name: string) {
|
|||||||
return path.join(tempDirectory, path.basename(filePath))
|
return path.join(tempDirectory, path.basename(filePath))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFilesFromDirectories(filePaths: string[]): string[] {
|
export async function getFilesFromDirectoriesAndURLs(
|
||||||
|
filePaths: string[]
|
||||||
|
): Promise<string[]> {
|
||||||
const fullPathSet: Set<string> = new Set<string>()
|
const fullPathSet: Set<string> = new Set<string>()
|
||||||
|
|
||||||
filePaths.forEach((fileName) => {
|
let fileCounter = 0
|
||||||
|
for (const fileName of filePaths) {
|
||||||
try {
|
try {
|
||||||
if (fs.lstatSync(fileName).isDirectory()) {
|
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()) {
|
||||||
recurisveManifestGetter(fileName).forEach((file) => {
|
recurisveManifestGetter(fileName).forEach((file) => {
|
||||||
fullPathSet.add(file)
|
fullPathSet.add(file)
|
||||||
})
|
})
|
||||||
@@ -86,9 +108,86 @@ export function getFilesFromDirectories(filePaths: string[]): string[] {
|
|||||||
`Exception occurred while reading the file ${fileName}: ${ex}`
|
`Exception occurred while reading the file ${fileName}: ${ex}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
return Array.from(fullPathSet)
|
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}
|
||||||
}
|
}
|
||||||
|
|
||||||
function recurisveManifestGetter(dirName: string): string[] {
|
function recurisveManifestGetter(dirName: string): string[] {
|
||||||
|
|||||||
@@ -1,12 +1,29 @@
|
|||||||
import subprocess, sys
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
kind = sys.argv[1]
|
|
||||||
name = sys.argv[2]
|
|
||||||
namespace = 'test-' + sys.argv[3]
|
|
||||||
|
|
||||||
try:
|
def delete(kind, name, namespace):
|
||||||
print('kubectl delete ' + kind + ' ' + name + ' -n ' + namespace)
|
try:
|
||||||
deletion = subprocess.Popen(['kubectl', 'delete', kind, name, '--namespace', namespace])
|
if (name == "all"):
|
||||||
result, err = deletion.communicate()
|
print('kubectl delete --all' + kind + ' -n ' + namespace)
|
||||||
except Exception as ex:
|
deletion = subprocess.Popen(
|
||||||
print('Error occured during deletion', ex)
|
['kubectl', 'delete', kind, name, '--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 = 'test-' + sys.argv[3]
|
||||||
|
delete(kind, name, namespace)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
|
|||||||
@@ -1,33 +1,252 @@
|
|||||||
import os, sys, json
|
from operator import truediv
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from unicodedata import name
|
||||||
|
|
||||||
RESULT = 'false'
|
# This integration test is used to confirm that k8s resources of a specified name, type, and configuration have been deployed.
|
||||||
k8_object = None
|
# Expected configurations are fed into the python script as command-line arguments and are compared to the configuration of resources that have been deployed.
|
||||||
kind = sys.argv[1]
|
|
||||||
name = sys.argv[2]
|
|
||||||
color = sys.argv[3]
|
|
||||||
namespace = 'test-' + sys.argv[4]
|
|
||||||
|
|
||||||
print('kubectl get '+kind+' '+name+' -n '+namespace+' -o json')
|
# args will be formatted like labels=testkey:testValue,otherKey=otherValue
|
||||||
|
# or for singular ones, just with containerName=container
|
||||||
|
|
||||||
try:
|
|
||||||
k8_object = json.load(os.popen('kubectl get '+kind+' '+name+' -n '+namespace+' -o json'))
|
|
||||||
except:
|
|
||||||
sys.exit(kind+' '+name+' not created')
|
|
||||||
|
|
||||||
try:
|
kindKey = "kind"
|
||||||
if kind == 'Deployment' and k8_object['spec']['selector']['matchLabels']['k8s.deploy.color'] == str(color):
|
nameKey = "name"
|
||||||
RESULT = 'true'
|
containerKey = "containerName"
|
||||||
if kind == 'Service' and k8_object['spec']['selector']['k8s.deploy.color'] == str(color):
|
labelsKey = "labels"
|
||||||
RESULT = 'true'
|
annotationsKey = "annotations"
|
||||||
if kind == 'Ingress':
|
selectorLabelsKey = "selectorLabels"
|
||||||
suffix = ''
|
namespaceKey = "namespace"
|
||||||
if str(color) == 'green':
|
ingressServicesKey = "ingressServices"
|
||||||
suffix = '-green'
|
tsServicesKey = "tsServices"
|
||||||
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':
|
privateKey = "private"
|
||||||
RESULT = 'true'
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if RESULT=='false':
|
|
||||||
sys.exit(kind+' '+name+' not labelled properly')
|
def parseArgs(sysArgs):
|
||||||
print('Test passed')
|
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 annotationsKey in argsDict:
|
||||||
|
argsDict[annotationsKey] = stringListToDict(
|
||||||
|
argsDict[annotationsKey].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(",")
|
||||||
|
|
||||||
|
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:
|
||||||
|
dictMatch, msg = compareDicts(
|
||||||
|
deployment['metadata']['annotations'], parsedArgs[annotationsKey], annotationsKey)
|
||||||
|
if not dictMatch:
|
||||||
|
return dictMatch, 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:
|
||||||
|
dictMatch, msg = compareDicts(
|
||||||
|
service['metadata']['annotations'], parsedArgs[annotationsKey], annotationsKey)
|
||||||
|
if not dictMatch:
|
||||||
|
return dictMatch, 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 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())
|
||||||
|
|||||||
+12
-6
@@ -16,7 +16,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
image: nginx:1.14.2
|
image: nginx
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
---
|
---
|
||||||
@@ -32,7 +32,7 @@ spec:
|
|||||||
port: 80
|
port: 80
|
||||||
targetPort: 80
|
targetPort: 80
|
||||||
---
|
---
|
||||||
apiVersion: networking.k8s.io/v1beta1
|
apiVersion: networking.k8s.io/v1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
name: nginx-ingress
|
name: nginx-ingress
|
||||||
@@ -43,10 +43,16 @@ spec:
|
|||||||
- http:
|
- http:
|
||||||
paths:
|
paths:
|
||||||
- path: /testpath
|
- path: /testpath
|
||||||
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
serviceName: nginx-service
|
service:
|
||||||
servicePort: 80
|
name: nginx-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
- path: /testpath2
|
- path: /testpath2
|
||||||
|
pathType: Prefix
|
||||||
backend:
|
backend:
|
||||||
serviceName: unrouted-service
|
service:
|
||||||
servicePort: 80
|
name: unrouted-service
|
||||||
|
port:
|
||||||
|
number: 80
|
||||||
+1
-1
@@ -16,7 +16,7 @@ spec:
|
|||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
image: nginx:1.14.2
|
image: nginx
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
---
|
---
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
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