mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-06-22 03:09:27 +08:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d200da279 | |||
| df58fb461e | |||
| a999ffcd6c | |||
| 00795b0b56 | |||
| d565a17533 | |||
| 1811836de2 | |||
| 10d9433b15 | |||
| 52dfbef986 | |||
| 074d812926 | |||
| e10b599478 | |||
| 93550c22f0 | |||
| 1fea8281df | |||
| 1b1edcdfc7 | |||
| 8cbe18c310 | |||
| 8efbc8ba92 | |||
| 699a70732d | |||
| a1d061da9d | |||
| 7c36b75ebe | |||
| 2f2901757b | |||
| 4aba7c26f3 | |||
| d6508445a1 | |||
| a462095a3c |
@@ -0,0 +1,18 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- '*'
|
||||
- package-ecosystem: github-actions
|
||||
directory: .github/workflows
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- '*'
|
||||
@@ -10,10 +10,13 @@ jobs:
|
||||
CodeQL-Build:
|
||||
# CodeQL runs on ubuntu-latest and windows-latest
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
@@ -21,7 +24,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@05963f47d870e2cb19a537396c1f668a348c7d8f #v3.24.8
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
@@ -29,7 +32,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@05963f47d870e2cb19a537396c1f668a348c7d8f #v3.24.8
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -43,4 +46,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@05963f47d870e2cb19a537396c1f668a348c7d8f #v3.24.8
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
name: Create release PR
|
||||
name: Release Project
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- CHANGELOG.md
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release:
|
||||
description: 'Define release version (ex: v1, v2, v3)'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
release-pr:
|
||||
uses: OliverMKing/javascript-release-workflow/.github/workflows/release-pr.yml@main
|
||||
release:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
uses: Azure/action-release-workflows/.github/workflows/release_js_project.yaml@v1
|
||||
with:
|
||||
release: ${{ github.event.inputs.release }}
|
||||
changelogPath: ./CHANGELOG.md
|
||||
|
||||
@@ -64,9 +64,13 @@ jobs:
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
test/integration/manifests/manifest_test_dir/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
|
||||
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment3 containerName=nginx:1.14.2 labels=app:nginx3,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_basic selectorLabels=app:nginx3
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service3 labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_basic selectorLabels=app:nginx3
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
minikube-version: 1.31.2
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
minikube-version: 1.31.2
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
@@ -13,7 +13,7 @@ on:
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
run: |
|
||||
set +x
|
||||
# create cluster
|
||||
az group create --location eastus --name ${{ env.NAMESPACE }}
|
||||
az group create --location eastus2 --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 }}
|
||||
|
||||
@@ -63,6 +63,7 @@ jobs:
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
test/integration/manifests/test2.yml
|
||||
action: deploy
|
||||
private-cluster: true
|
||||
resource-group: ${{ env.NAMESPACE }}
|
||||
@@ -73,6 +74,9 @@ jobs:
|
||||
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
|
||||
|
||||
python test/integration/k8s-deploy-test.py private=${{ env.NAMESPACE }} namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment2 containerName=nginx:1.14.2 labels=app:nginx2,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Cluster_Integration_Tests_-_private_cluster selectorLabels=app:nginx2
|
||||
python test/integration/k8s-deploy-test.py private=${{ env.NAMESPACE }} namespace=${{ env.NAMESPACE }} kind=Service name=nginx-service2 labels=workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Cluster_Integration_Tests_-_private_cluster selectorLabels=app:nginx2
|
||||
|
||||
- name: Clean up AKS cluster
|
||||
if: ${{ always() }}
|
||||
run: |
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
name: Minikube Integration Tests - resource annotation
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
NAMESPACE: test-${{ github.run_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Install conntrack
|
||||
run: sudo apt-get install -y conntrack
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- uses: Azure/setup-kubectl@v3
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: medyagh/setup-minikube@latest
|
||||
with:
|
||||
minikube-version: 1.24.0
|
||||
kubernetes-version: 1.22.3
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns ${{ env.NAMESPACE }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for pod with resource annotation enabled by default
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deployments is created with additional resource annotation
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 labels=app:nginx,workflow:actions.github.com-k8s-deploy,workflowFriendlyName:Minikube_Integration_Tests_-_resource_annotation selectorLabels=app:nginx annotations=actions.github.com/k8s-deploy,deployment.kubernetes.io/revision,kubectl.kubernetes.io/last-applied-configuration
|
||||
|
||||
- name: Cleaning previously created deployment
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'all' ${{ env.NAMESPACE }}
|
||||
|
||||
- name: Executing deploy action for pod with resource annotation disabled
|
||||
uses: ./
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test.yml
|
||||
action: deploy
|
||||
annotate-resources: false
|
||||
|
||||
- name: Checking if deployment is created without additional resource annotation
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py namespace=${{ env.NAMESPACE }} kind=Deployment name=nginx-deployment containerName=nginx:1.14.2 selectorLabels=app:nginx annotations=deployment.kubernetes.io/revision,kubectl.kubernetes.io/last-applied-configuration
|
||||
@@ -1,10 +0,0 @@
|
||||
name: Tag and create release draft
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- releases/*
|
||||
|
||||
jobs:
|
||||
tag-and-release:
|
||||
uses: OliverMKing/javascript-release-workflow/.github/workflows/tag-and-release.yml@main
|
||||
+2
-1
@@ -2,5 +2,6 @@ node_modules
|
||||
|
||||
.DS_Store
|
||||
.idea
|
||||
lib/
|
||||
|
||||
coverage/
|
||||
coverage/
|
||||
@@ -0,0 +1,28 @@
|
||||
# Changelog
|
||||
|
||||
## [5.0.0] - 2024-03-12
|
||||
|
||||
### Changed
|
||||
|
||||
- #309 Updated to Node20 and upgraded release workflows to @v1 tag
|
||||
- #306 update release workflow to use new prefix, remove deprecated release
|
||||
- #303 fix: ensure imageNames are not empty strings
|
||||
- #299 bump release workflow sha
|
||||
- #298 bump minikube to fix runner deps
|
||||
- #297 update release workflow
|
||||
|
||||
### Added
|
||||
|
||||
- #304 add v prefix for version tagging
|
||||
- #302 adding ncc to build
|
||||
- #301 adding release workflow artifact fix
|
||||
|
||||
## [4.10.0] - 2023-10-30
|
||||
|
||||
### Added
|
||||
|
||||
- #287 Make annotating resources optional
|
||||
- #283 Fix “Service” route-method of the Blue-Green strategy with some manifest files
|
||||
- #281 bump codeql to node 16
|
||||
- #279 upgrade codeql
|
||||
- #276 Fixes multiple namespaces bug
|
||||
@@ -113,9 +113,13 @@ Following are the key capabilities of this action:
|
||||
<td>force </br></br>(Optional)</td>
|
||||
<td>Deploy when a previous deployment already exists. If true then '--force' argument is added to the apply command. Using '--force' argument is not recommended in production.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotate-resources</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the resources or not. If set to false all annotations are skipped completely.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotate-namespace</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the namespace resources object or not</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the namespace resources object or not. Ignored when annotate-resources is set to false.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>skip-tls-verify</br></br>(Optional)</td>
|
||||
@@ -128,7 +132,7 @@ Following are the key capabilities of this action:
|
||||
### Basic deployment (without any deployment strategy)
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v5
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
manifests: |
|
||||
@@ -142,7 +146,7 @@ Following are the key capabilities of this action:
|
||||
### Private cluster deployment
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v5
|
||||
with:
|
||||
resource-group: yourResourceGroup
|
||||
name: yourClusterName
|
||||
@@ -162,7 +166,7 @@ Following are the key capabilities of this action:
|
||||
### Canary deployment without service mesh
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v5
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
@@ -181,7 +185,7 @@ Following are the key capabilities of this action:
|
||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v5
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
@@ -199,7 +203,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
### Canary deployment based on Service Mesh Interface
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v5
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
@@ -220,7 +224,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v5
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }} '
|
||||
@@ -239,7 +243,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
### Blue-Green deployment with different route methods
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v5
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
@@ -259,7 +263,7 @@ To promote/reject the canary created by the above snippet, the following YAML sn
|
||||
To promote/reject the green workload created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v5
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
@@ -288,7 +292,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
@@ -300,23 +304,23 @@ jobs:
|
||||
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
|
||||
- uses: azure/setup-kubectl@v2.0
|
||||
- uses: azure/setup-kubectl@v4
|
||||
|
||||
# Set the target AKS cluster.
|
||||
- uses: Azure/aks-set-context@v1
|
||||
- uses: Azure/aks-set-context@v4
|
||||
with:
|
||||
creds: '${{ secrets.AZURE_CREDENTIALS }}'
|
||||
cluster-name: contoso
|
||||
resource-group: contoso-rg
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1.1
|
||||
- uses: Azure/k8s-create-secret@v4
|
||||
with:
|
||||
container-registry-url: contoso.azurecr.io
|
||||
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v4
|
||||
- uses: Azure/k8s-deploy@v5
|
||||
with:
|
||||
action: deploy
|
||||
manifests: |
|
||||
@@ -337,7 +341,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
@@ -349,13 +353,13 @@ jobs:
|
||||
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
|
||||
- uses: azure/setup-kubectl@v2.0
|
||||
- uses: azure/setup-kubectl@v4
|
||||
|
||||
- uses: Azure/k8s-set-context@v2
|
||||
- uses: Azure/k8s-set-context@v4
|
||||
with:
|
||||
kubeconfig: ${{ secrets.KUBE_CONFIG }}
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1.1
|
||||
- uses: Azure/k8s-create-secret@v4
|
||||
with:
|
||||
container-registry-url: contoso.azurecr.io
|
||||
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
@@ -387,7 +391,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
@@ -419,16 +423,16 @@ jobs:
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- uses: azure/setup-kubectl@v2.0
|
||||
- uses: azure/setup-kubectl@v4
|
||||
|
||||
# Set the target AKS cluster.
|
||||
- uses: Azure/aks-set-context@v1
|
||||
- uses: Azure/aks-set-context@v4
|
||||
with:
|
||||
creds: '${{ secrets.AZURE_CREDENTIALS }}'
|
||||
cluster-name: contoso
|
||||
resource-group: contoso-rg
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1.1
|
||||
- uses: Azure/k8s-create-secret@v4
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
container-registry-url: contoso.azurecr.io
|
||||
@@ -436,7 +440,7 @@ jobs:
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: azure/k8s-bake@v2
|
||||
- uses: azure/k8s-bake@v3
|
||||
with:
|
||||
renderEngine: 'helm'
|
||||
helmChart: './aks-helloworld/'
|
||||
@@ -446,7 +450,7 @@ jobs:
|
||||
helm-version: 'latest'
|
||||
id: bake
|
||||
|
||||
- uses: Azure/k8s-deploy@v1.2
|
||||
- uses: Azure/k8s-deploy@v5
|
||||
with:
|
||||
action: deploy
|
||||
manifests: ${{ steps.bake.outputs.manifestsBundle }}
|
||||
|
||||
+6
-2
@@ -59,8 +59,12 @@ inputs:
|
||||
description: 'Github token'
|
||||
default: ${{ github.token }}
|
||||
required: true
|
||||
annotate-resources:
|
||||
description: 'Annotate the resources. If set to false all annotations are skipped completely.'
|
||||
required: false
|
||||
default: true
|
||||
annotate-namespace:
|
||||
description: 'Annotate the target namespace'
|
||||
description: 'Annotate the target namespace. Ignored when annotate-resources is set to false.'
|
||||
required: false
|
||||
default: true
|
||||
private-cluster:
|
||||
@@ -80,5 +84,5 @@ inputs:
|
||||
branding:
|
||||
color: 'green'
|
||||
runs:
|
||||
using: 'node16'
|
||||
using: 'node20'
|
||||
main: 'lib/index.js'
|
||||
|
||||
-24092
File diff suppressed because one or more lines are too long
Generated
+2974
-6593
File diff suppressed because it is too large
Load Diff
+9
-7
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "k8s-deploy-action",
|
||||
"version": "0.0.0",
|
||||
"version": "5.0.0",
|
||||
"author": "Deepak Sattiraju",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "npx ncc build src/run.ts -o lib",
|
||||
"prebuild": "npm i @vercel/ncc",
|
||||
"build": "ncc build src/run.ts -o lib",
|
||||
"test": "jest",
|
||||
"coverage": "jest --coverage=true",
|
||||
"format": "prettier --write .",
|
||||
@@ -18,16 +19,17 @@
|
||||
"@octokit/core": "^3.5.1",
|
||||
"@octokit/plugin-retry": "^3.0.9",
|
||||
"@types/minipass": "^3.1.2",
|
||||
"js-yaml": "3.13.1"
|
||||
"js-yaml": "3.13.1",
|
||||
"minimist": "^1.2.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.0",
|
||||
"@types/js-yaml": "^3.12.7",
|
||||
"@types/node": "^12.20.41",
|
||||
"@vercel/ncc": "^0.36.1",
|
||||
"jest": "^26.0.0",
|
||||
"prettier": "^2.7.1",
|
||||
"ts-jest": "^26.0.0",
|
||||
"typescript": "3.9.5"
|
||||
"jest": "^29.7.0",
|
||||
"prettier": "^2.8.8",
|
||||
"ts-jest": "^29.2.3",
|
||||
"typescript": "5.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,17 +65,10 @@ export async function deploy(
|
||||
|
||||
// annotate resources
|
||||
core.startGroup('Annotating resources')
|
||||
let allPods
|
||||
try {
|
||||
allPods = JSON.parse((await kubectl.getAllPods()).stdout)
|
||||
} catch (e) {
|
||||
core.debug(`Unable to parse pods: ${e}`)
|
||||
}
|
||||
await annotateAndLabelResources(
|
||||
deployedManifestFiles,
|
||||
kubectl,
|
||||
resourceTypes,
|
||||
allPods
|
||||
resourceTypes
|
||||
)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
+2
-19
@@ -129,19 +129,13 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
|
||||
|
||||
// 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)
|
||||
await annotateAndLabelResources(filesToAnnotate, kubectl, resources)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
@@ -219,17 +213,6 @@ async function promoteBlueGreen(kubectl: Kubectl, manifests: string[]) {
|
||||
|
||||
// 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
|
||||
)
|
||||
await annotateAndLabelResources(deployedManifestFiles, kubectl, resources)
|
||||
core.endGroup()
|
||||
}
|
||||
|
||||
@@ -10,12 +10,7 @@ import {Kubectl, Resource} from '../types/kubectl'
|
||||
import {deployPodCanary} from './canary/podCanaryHelper'
|
||||
import {deploySMICanary} from './canary/smiCanaryHelper'
|
||||
import {DeploymentConfig} from '../types/deploymentConfig'
|
||||
import {
|
||||
deployBlueGreen,
|
||||
deployBlueGreenIngress,
|
||||
deployBlueGreenService
|
||||
} from './blueGreen/deploy'
|
||||
import {deployBlueGreenSMI} from './blueGreen/deploy'
|
||||
import {deployBlueGreen} from './blueGreen/deploy'
|
||||
import {DeploymentStrategy} from '../types/deploymentStrategy'
|
||||
import * as core from '@actions/core'
|
||||
import {
|
||||
@@ -39,7 +34,6 @@ import {
|
||||
normalizeWorkflowStrLabel
|
||||
} from '../utilities/githubUtils'
|
||||
import {getDeploymentConfig} from '../utilities/dockerUtils'
|
||||
import {deploy} from '../actions/deploy'
|
||||
import {DeployResult} from '../types/deployResult'
|
||||
|
||||
export async function deployManifests(
|
||||
@@ -147,8 +141,7 @@ export async function checkManifestStability(
|
||||
export async function annotateAndLabelResources(
|
||||
files: string[],
|
||||
kubectl: Kubectl,
|
||||
resourceTypes: Resource[],
|
||||
allPods: any
|
||||
resourceTypes: Resource[]
|
||||
) {
|
||||
const defaultWorkflowFileName = 'k8s-deploy-failed-workflow-annotation'
|
||||
const githubToken = core.getInput('token')
|
||||
@@ -163,15 +156,20 @@ export async function annotateAndLabelResources(
|
||||
const deploymentConfig = await getDeploymentConfig()
|
||||
const annotationKeyLabel = getWorkflowAnnotationKeyLabel()
|
||||
|
||||
await annotateResources(
|
||||
files,
|
||||
kubectl,
|
||||
resourceTypes,
|
||||
allPods,
|
||||
annotationKeyLabel,
|
||||
workflowFilePath,
|
||||
deploymentConfig
|
||||
).catch((err) => core.warning(`Failed to annotate resources: ${err} `))
|
||||
const shouldAnnotateResources = !(
|
||||
core.getInput('annotate-resources').toLowerCase() === 'false'
|
||||
)
|
||||
|
||||
if (shouldAnnotateResources) {
|
||||
await annotateResources(
|
||||
files,
|
||||
kubectl,
|
||||
resourceTypes,
|
||||
annotationKeyLabel,
|
||||
workflowFilePath,
|
||||
deploymentConfig
|
||||
).catch((err) => core.warning(`Failed to annotate resources: ${err} `))
|
||||
}
|
||||
|
||||
await labelResources(files, kubectl, annotationKeyLabel).catch((err) =>
|
||||
core.warning(`Failed to label resources: ${err}`)
|
||||
@@ -182,7 +180,6 @@ async function annotateResources(
|
||||
files: string[],
|
||||
kubectl: Kubectl,
|
||||
resourceTypes: Resource[],
|
||||
allPods: any,
|
||||
annotationKey: string,
|
||||
workflowFilePath: string,
|
||||
deploymentConfig: DeploymentConfig
|
||||
@@ -226,11 +223,13 @@ async function annotateResources(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const annotateResult = await kubectl.annotateFiles(
|
||||
file,
|
||||
annotationKeyValStr
|
||||
annotationKeyValStr,
|
||||
namespace
|
||||
)
|
||||
annotateResults.push(annotateResult)
|
||||
} catch (e) {
|
||||
@@ -249,8 +248,7 @@ async function annotateResources(
|
||||
resource.type,
|
||||
resource.name,
|
||||
resource.namespace,
|
||||
annotationKeyValStr,
|
||||
allPods
|
||||
annotationKeyValStr
|
||||
)
|
||||
).forEach((execResult) => annotateResults.push(execResult))
|
||||
}
|
||||
|
||||
@@ -39,17 +39,6 @@ const testNamespace = 'testNamespace'
|
||||
const defaultNamespace = 'default'
|
||||
const otherNamespace = 'otherns'
|
||||
describe('Kubectl class', () => {
|
||||
describe('default namespace behavior', () => {
|
||||
const kubectl = new Kubectl(kubectlPath, defaultNamespace)
|
||||
const execReturn = {exitCode: 0, stdout: 'Output', stderr: ''}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(exec, 'getExecOutput').mockImplementation(async () => {
|
||||
return execReturn
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with a success exec return in testNamespace', () => {
|
||||
const kubectl = new Kubectl(kubectlPath, testNamespace)
|
||||
const execReturn = {exitCode: 0, stdout: 'Output', stderr: ''}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import {PrivateKubectl} from './privatekubectl'
|
||||
import * as fileUtils from '../utilities/fileUtils'
|
||||
import * as fs from 'fs'
|
||||
import {
|
||||
PrivateKubectl,
|
||||
extractFileNames,
|
||||
replaceFileNamesWithShallowNamesRelativeToTemp
|
||||
} from './privatekubectl'
|
||||
import * as exec from '@actions/exec'
|
||||
|
||||
describe('Private kubectl', () => {
|
||||
const testString = `kubectl annotate -f test.yml,test2.yml,test3.yml -f test4.yml --filename test5.yml actions.github.com/k8s-deploy={"run":"3498366832","repository":"jaiveerk/k8s-deploy","workflow":"Minikube Integration Tests - private cluster","workflowFileName":"run-integration-tests-private.yml","jobName":"run-integration-test","createdBy":"jaiveerk","runUri":"https://github.com/jaiveerk/k8s-deploy/actions/runs/3498366832","commit":"c63b323186ea1320a31290de6dcc094c06385e75","lastSuccessRunCommit":"NA","branch":"refs/heads/main","deployTimestamp":1668787848577,"dockerfilePaths":{"nginx:1.14.2":""},"manifestsPaths":["https://github.com/jaiveerk/k8s-deploy/blob/c63b323186ea1320a31290de6dcc094c06385e75/test/integration/manifests/test.yml"],"helmChartPaths":[],"provider":"GitHub"} --overwrite --namespace test-3498366832`
|
||||
const testString = `kubectl annotate -f /tmp/testdir/test.yml,/tmp/test2.yml,/tmp/testdir/subdir/test3.yml -f /tmp/test4.yml --filename /tmp/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/test.yml"],"helmChartPaths":[],"provider":"GitHub"} --overwrite --namespace test-3498366832`
|
||||
const mockKube = new PrivateKubectl(
|
||||
'kubectlPath',
|
||||
'namespace',
|
||||
@@ -11,9 +17,32 @@ describe('Private kubectl', () => {
|
||||
'resourceName'
|
||||
)
|
||||
|
||||
const spy = jest
|
||||
.spyOn(fileUtils, 'getTempDirectory')
|
||||
.mockImplementation(() => {
|
||||
return '/tmp'
|
||||
})
|
||||
|
||||
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {})
|
||||
jest.spyOn(fs, 'readFileSync').mockImplementation((filename) => {
|
||||
return 'test contents'
|
||||
})
|
||||
|
||||
it('should extract filenames correctly', () => {
|
||||
expect(mockKube.extractFilesnames(testString)).toEqual(
|
||||
'test.yml test2.yml test3.yml test4.yml test5.yml'
|
||||
expect(extractFileNames(testString)).toEqual([
|
||||
'/tmp/testdir/test.yml',
|
||||
'/tmp/test2.yml',
|
||||
'/tmp/testdir/subdir/test3.yml',
|
||||
'/tmp/test4.yml',
|
||||
'/tmp/test5.yml'
|
||||
])
|
||||
})
|
||||
|
||||
it('should replace filenames with shallow names for relative locations in tmp correctly', () => {
|
||||
expect(
|
||||
replaceFileNamesWithShallowNamesRelativeToTemp(testString)
|
||||
).toEqual(
|
||||
`kubectl annotate -f testdir-test.yml,test2.yml,testdir-subdir-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/test.yml"],"helmChartPaths":[],"provider":"GitHub"} --overwrite --namespace test-3498366832`
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
+86
-99
@@ -5,6 +5,7 @@ import * as core from '@actions/core'
|
||||
import * as os from 'os'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import {getTempDirectory} from '../utilities/fileUtils'
|
||||
|
||||
export class PrivateKubectl extends Kubectl {
|
||||
protected async execute(args: string[], silent: boolean = false) {
|
||||
@@ -18,8 +19,7 @@ export class PrivateKubectl extends Kubectl {
|
||||
}
|
||||
|
||||
if (this.containsFilenames(kubectlCmd)) {
|
||||
// For private clusters, files will referenced solely by their basename
|
||||
kubectlCmd = this.replaceFilnamesWithBasenames(kubectlCmd)
|
||||
kubectlCmd = replaceFileNamesWithShallowNamesRelativeToTemp(kubectlCmd)
|
||||
addFileFlag = true
|
||||
}
|
||||
|
||||
@@ -43,22 +43,9 @@ export class PrivateKubectl extends Kubectl {
|
||||
]
|
||||
|
||||
if (addFileFlag) {
|
||||
const filenames = this.extractFilesnames(kubectlCmd).split(' ')
|
||||
|
||||
const tempDirectory =
|
||||
process.env['runner.tempDirectory'] || os.tmpdir() + '/manifests'
|
||||
eo.cwd = tempDirectory
|
||||
const tempDirectory = getTempDirectory()
|
||||
eo.cwd = path.join(tempDirectory, 'manifests')
|
||||
privateClusterArgs.push(...['--file', '.'])
|
||||
|
||||
let filenamesArr = filenames[0].split(',')
|
||||
for (let index = 0; index < filenamesArr.length; index++) {
|
||||
const file = filenamesArr[index]
|
||||
|
||||
if (!file) {
|
||||
continue
|
||||
}
|
||||
this.moveFileToTempManifestDir(file)
|
||||
}
|
||||
}
|
||||
|
||||
core.debug(
|
||||
@@ -95,89 +82,89 @@ export class PrivateKubectl extends Kubectl {
|
||||
} as ExecOutput
|
||||
}
|
||||
|
||||
private replaceFilnamesWithBasenames(kubectlCmd: string) {
|
||||
let exFilenames = this.extractFilesnames(kubectlCmd)
|
||||
let filenames = exFilenames.split(' ')
|
||||
let filenamesArr = filenames[0].split(',')
|
||||
|
||||
for (let index = 0; index < filenamesArr.length; index++) {
|
||||
filenamesArr[index] = path.basename(filenamesArr[index])
|
||||
}
|
||||
|
||||
let baseFilenames = filenamesArr.join()
|
||||
|
||||
let result = kubectlCmd.replace(exFilenames, baseFilenames)
|
||||
return result
|
||||
}
|
||||
|
||||
public extractFilesnames(strToParse: string) {
|
||||
const fileNames: string[] = []
|
||||
const argv = minimist(strToParse.split(' '))
|
||||
const fArg = 'f'
|
||||
const filenameArg = 'filename'
|
||||
|
||||
fileNames.push(...this.extractFilesFromMinimist(argv, fArg))
|
||||
fileNames.push(...this.extractFilesFromMinimist(argv, filenameArg))
|
||||
|
||||
return fileNames.join(' ')
|
||||
}
|
||||
|
||||
private extractFilesFromMinimist(argv, arg: string): string[] {
|
||||
if (!argv[arg]) {
|
||||
return []
|
||||
}
|
||||
const toReturn: string[] = []
|
||||
if (typeof argv[arg] === 'string') {
|
||||
toReturn.push(...argv[arg].split(','))
|
||||
} else {
|
||||
for (const value of argv[arg] as string[]) {
|
||||
toReturn.push(...value.split(','))
|
||||
}
|
||||
}
|
||||
|
||||
return toReturn
|
||||
}
|
||||
|
||||
private containsFilenames(str: string) {
|
||||
return str.includes('-f ') || str.includes('filename ')
|
||||
}
|
||||
|
||||
private createTempManifestsDirectory() {
|
||||
const manifestsDir = '/tmp/manifests'
|
||||
if (!fs.existsSync('/tmp/manifests')) {
|
||||
fs.mkdirSync('/tmp/manifests', {recursive: true})
|
||||
}
|
||||
}
|
||||
|
||||
private moveFileToTempManifestDir(file: string) {
|
||||
this.createTempManifestsDirectory()
|
||||
if (!fs.existsSync('/tmp/' + file)) {
|
||||
core.debug(
|
||||
'/tmp/' +
|
||||
file +
|
||||
' does not exist, and therefore cannot be moved to the manifest directory'
|
||||
)
|
||||
}
|
||||
|
||||
fs.copyFile('/tmp/' + file, '/tmp/manifests/' + file, function (err) {
|
||||
if (err) {
|
||||
core.debug(
|
||||
'Could not rename ' +
|
||||
'/tmp/' +
|
||||
file +
|
||||
' to ' +
|
||||
'/tmp/manifests/' +
|
||||
file +
|
||||
' ERROR: ' +
|
||||
err
|
||||
)
|
||||
return
|
||||
}
|
||||
core.debug(
|
||||
"Successfully moved file '" +
|
||||
file +
|
||||
"' from /tmp to /tmp/manifest directory"
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function createTempManifestsDirectory(): string {
|
||||
const manifestsDirPath = path.join(getTempDirectory(), 'manifests')
|
||||
if (!fs.existsSync(manifestsDirPath)) {
|
||||
fs.mkdirSync(manifestsDirPath, {recursive: true})
|
||||
}
|
||||
|
||||
return manifestsDirPath
|
||||
}
|
||||
|
||||
export function replaceFileNamesWithShallowNamesRelativeToTemp(
|
||||
kubectlCmd: string
|
||||
) {
|
||||
let filenames = extractFileNames(kubectlCmd)
|
||||
core.debug(`filenames originally provided in kubectl command: ${filenames}`)
|
||||
let relativeShallowNames = filenames.map((filename) => {
|
||||
const relativeName = path.relative(getTempDirectory(), filename)
|
||||
|
||||
const relativePathElements = relativeName.split(path.sep)
|
||||
|
||||
const shallowName = relativePathElements.join('-')
|
||||
|
||||
// make manifests dir in temp if it doesn't already exist
|
||||
const manifestsTempDir = createTempManifestsDirectory()
|
||||
|
||||
const shallowPath = path.join(manifestsTempDir, shallowName)
|
||||
core.debug(
|
||||
`moving contents from ${filename} to shallow location at ${shallowPath}`
|
||||
)
|
||||
|
||||
core.debug(`reading contents from ${filename}`)
|
||||
const contents = fs.readFileSync(filename).toString()
|
||||
|
||||
core.debug(`writing contents to new path ${shallowPath}`)
|
||||
fs.writeFileSync(shallowPath, contents)
|
||||
|
||||
return shallowName
|
||||
})
|
||||
|
||||
let result = kubectlCmd
|
||||
if (filenames.length != relativeShallowNames.length) {
|
||||
throw Error(
|
||||
'replacing filenames with relative path from temp dir, ' +
|
||||
filenames.length +
|
||||
' filenames != ' +
|
||||
relativeShallowNames.length +
|
||||
'basenames'
|
||||
)
|
||||
}
|
||||
for (let index = 0; index < filenames.length; index++) {
|
||||
result = result.replace(filenames[index], relativeShallowNames[index])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function extractFileNames(strToParse: string) {
|
||||
const fileNames: string[] = []
|
||||
const argv = minimist(strToParse.split(' '))
|
||||
const fArg = 'f'
|
||||
const filenameArg = 'filename'
|
||||
|
||||
fileNames.push(...extractFilesFromMinimist(argv, fArg))
|
||||
fileNames.push(...extractFilesFromMinimist(argv, filenameArg))
|
||||
|
||||
return fileNames
|
||||
}
|
||||
|
||||
export function 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(','))
|
||||
}
|
||||
}
|
||||
|
||||
return toReturn
|
||||
}
|
||||
|
||||
@@ -23,7 +23,11 @@ export async function getDeploymentConfig(): Promise<DeploymentConfig> {
|
||||
)
|
||||
}
|
||||
|
||||
const imageNames = core.getInput('images').split('\n') || []
|
||||
const imageNames =
|
||||
core
|
||||
.getInput('images')
|
||||
.split('\n')
|
||||
.filter((image) => image.length > 0) || []
|
||||
const imageDockerfilePathMap: {[id: string]: string} = {}
|
||||
|
||||
const pullImages = !(core.getInput('pull-images').toLowerCase() === 'false')
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
import {
|
||||
getFilesFromDirectoriesAndURLs,
|
||||
getTempDirectory,
|
||||
urlFileKind,
|
||||
writeYamlFromURLToFile
|
||||
} from './fileUtils'
|
||||
import * as fileUtils from './fileUtils'
|
||||
|
||||
import * as yaml from 'js-yaml'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import {succeeded} from '../types/errorable'
|
||||
|
||||
const sampleYamlUrl =
|
||||
'https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/controllers/nginx-deployment.yaml'
|
||||
describe('File utils', () => {
|
||||
test('correctly parses a yaml file from a URL', async () => {
|
||||
const tempFile = await writeYamlFromURLToFile(sampleYamlUrl, 0)
|
||||
const tempFile = await fileUtils.writeYamlFromURLToFile(sampleYamlUrl, 0)
|
||||
const fileContents = fs.readFileSync(tempFile).toString()
|
||||
const inputObjects = yaml.safeLoadAll(fileContents)
|
||||
expect(inputObjects).toHaveLength(1)
|
||||
@@ -30,34 +24,34 @@ describe('File utils', () => {
|
||||
|
||||
const testPath = path.join('test', 'unit', 'manifests')
|
||||
await expect(
|
||||
getFilesFromDirectoriesAndURLs([testPath, badUrl])
|
||||
fileUtils.getFilesFromDirectoriesAndURLs([testPath, badUrl])
|
||||
).rejects.toThrow()
|
||||
})
|
||||
|
||||
it('detects files in nested directories and ignores non-manifest files and empty dirs', async () => {
|
||||
it('detects files in nested directories with the same name and ignores non-manifest files and empty dirs', async () => {
|
||||
const testPath = path.join('test', 'unit', 'manifests')
|
||||
const testSearch: string[] = await getFilesFromDirectoriesAndURLs([
|
||||
testPath,
|
||||
sampleYamlUrl
|
||||
])
|
||||
const testSearch: string[] =
|
||||
await fileUtils.getFilesFromDirectoriesAndURLs([
|
||||
testPath,
|
||||
sampleYamlUrl
|
||||
])
|
||||
|
||||
const expectedManifests = [
|
||||
'test/unit/manifests/manifest_test_dir/another_layer/deep-ingress.yaml',
|
||||
'test/unit/manifests/manifest_test_dir/another_layer/deep-service.yaml',
|
||||
'test/unit/manifests/manifest_test_dir/another_layer/test-ingress.yaml',
|
||||
'test/unit/manifests/manifest_test_dir/another_layer/nested-test-service.yaml',
|
||||
'test/unit/manifests/manifest_test_dir/nested-test-service.yaml',
|
||||
'test/unit/manifests/test-ingress.yml',
|
||||
'test/unit/manifests/test-ingress-new.yml',
|
||||
'test/unit/manifests/test-service.yml'
|
||||
]
|
||||
|
||||
// is there a more efficient way to test equality w random order?
|
||||
expect(testSearch).toHaveLength(8)
|
||||
expectedManifests.forEach((fileName) => {
|
||||
if (fileName.startsWith('test/unit')) {
|
||||
expect(testSearch).toContain(fileName)
|
||||
} else {
|
||||
expect(fileName.includes(urlFileKind)).toBe(true)
|
||||
expect(fileName.startsWith(getTempDirectory()))
|
||||
expect(fileName.includes(fileUtils.urlFileKind)).toBe(true)
|
||||
expect(fileName.startsWith(fileUtils.getTempDirectory()))
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -72,7 +66,7 @@ describe('File utils', () => {
|
||||
)
|
||||
|
||||
expect(
|
||||
getFilesFromDirectoriesAndURLs([badPath, goodPath])
|
||||
fileUtils.getFilesFromDirectoriesAndURLs([badPath, goodPath])
|
||||
).rejects.toThrowError()
|
||||
})
|
||||
|
||||
@@ -92,7 +86,7 @@ describe('File utils', () => {
|
||||
)
|
||||
|
||||
expect(
|
||||
await getFilesFromDirectoriesAndURLs([
|
||||
await fileUtils.getFilesFromDirectoriesAndURLs([
|
||||
outerPath,
|
||||
fileAtOuter,
|
||||
innerPath
|
||||
@@ -102,6 +96,24 @@ describe('File utils', () => {
|
||||
|
||||
it('throws an error for an invalid URL', async () => {
|
||||
const badUrl = 'https://www.github.com'
|
||||
await expect(writeYamlFromURLToFile(badUrl, 0)).rejects.toBeTruthy()
|
||||
await expect(
|
||||
fileUtils.writeYamlFromURLToFile(badUrl, 0)
|
||||
).rejects.toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('moving files to temp', () => {
|
||||
it('correctly moves the contents of a file to the temporary directory', () => {
|
||||
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {})
|
||||
jest.spyOn(fs, 'readFileSync').mockImplementation((filename) => {
|
||||
return 'test contents'
|
||||
})
|
||||
const originalFilePath = path.join('path', 'in', 'repo')
|
||||
|
||||
const output = fileUtils.moveFileToTmpDir(originalFilePath)
|
||||
|
||||
expect(output).toEqual(
|
||||
path.join(fileUtils.getTempDirectory(), '/path/in/repo')
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -23,7 +23,7 @@ export function writeObjectsToFile(inputObjects: any[]): string[] {
|
||||
const inputObjectString = JSON.stringify(inputObject)
|
||||
|
||||
if (inputObject?.metadata?.name) {
|
||||
const fileName = getManifestFileName(
|
||||
const fileName = getNewTempManifestFileName(
|
||||
inputObject.kind,
|
||||
inputObject.metadata.name
|
||||
)
|
||||
@@ -52,7 +52,7 @@ export function writeManifestToFile(
|
||||
): string {
|
||||
if (inputObjectString) {
|
||||
try {
|
||||
const fileName = getManifestFileName(kind, name)
|
||||
const fileName = getNewTempManifestFileName(kind, name)
|
||||
fs.writeFileSync(path.join(fileName), inputObjectString)
|
||||
return fileName
|
||||
} catch (ex) {
|
||||
@@ -63,7 +63,27 @@ export function writeManifestToFile(
|
||||
}
|
||||
}
|
||||
|
||||
function getManifestFileName(kind: string, name: string) {
|
||||
export function moveFileToTmpDir(originalFilepath: string) {
|
||||
const tempDirectory = getTempDirectory()
|
||||
const newPath = path.join(tempDirectory, originalFilepath)
|
||||
|
||||
core.debug(`reading original contents from path: ${originalFilepath}`)
|
||||
const contents = fs.readFileSync(originalFilepath).toString()
|
||||
|
||||
const dirName = path.dirname(newPath)
|
||||
if (!fs.existsSync(dirName)) {
|
||||
core.debug(`path ${dirName} doesn't exist yet, making new dir...`)
|
||||
fs.mkdirSync(dirName, {recursive: true})
|
||||
}
|
||||
core.debug(`writing contents to new path ${newPath}`)
|
||||
fs.writeFileSync(path.join(newPath), contents)
|
||||
|
||||
core.debug(`moved contents from ${originalFilepath} to ${newPath}`)
|
||||
|
||||
return newPath
|
||||
}
|
||||
|
||||
function getNewTempManifestFileName(kind: string, name: string) {
|
||||
const filePath = `${kind}_${name}_${getCurrentTime().toString()}`
|
||||
const tempDirectory = getTempDirectory()
|
||||
return path.join(tempDirectory, path.basename(filePath))
|
||||
@@ -130,7 +150,7 @@ export async function writeYamlFromURLToFile(
|
||||
)
|
||||
}
|
||||
|
||||
const targetPath = getManifestFileName(
|
||||
const targetPath = getNewTempManifestFileName(
|
||||
urlFileKind,
|
||||
fileNumber.toString()
|
||||
)
|
||||
|
||||
@@ -61,8 +61,7 @@ export async function annotateChildPods(
|
||||
resourceType: string,
|
||||
resourceName: string,
|
||||
namespace: string | undefined,
|
||||
annotationKeyValStr: string,
|
||||
allPods
|
||||
annotationKeyValStr: string
|
||||
): Promise<ExecOutput[]> {
|
||||
let owner = resourceName
|
||||
if (resourceType.toLowerCase().indexOf('deployment') > -1) {
|
||||
@@ -70,6 +69,14 @@ export async function annotateChildPods(
|
||||
}
|
||||
|
||||
const commandExecutionResults = []
|
||||
|
||||
let allPods
|
||||
try {
|
||||
allPods = JSON.parse((await kubectl.getAllPods()).stdout)
|
||||
} catch (e) {
|
||||
core.debug(`Unable to parse pods: ${e}`)
|
||||
}
|
||||
|
||||
if (allPods?.items && allPods.items?.length > 0) {
|
||||
allPods.items.forEach((pod) => {
|
||||
const owners = pod?.metadata?.ownerReferences
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import * as fileUtils from './fileUtils'
|
||||
import * as manifestUpdateUtils from './manifestUpdateUtils'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
|
||||
describe('manifestUpdateUtils', () => {
|
||||
jest.spyOn(fileUtils, 'moveFileToTmpDir').mockImplementation((filename) => {
|
||||
return path.join('/tmp', filename)
|
||||
})
|
||||
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {})
|
||||
jest.spyOn(fs, 'readFileSync').mockImplementation((filename) => {
|
||||
return 'test contents'
|
||||
})
|
||||
|
||||
it('should place all files within the temp dir with the same path that they have in the repo', () => {
|
||||
const originalFilePaths: string[] = [
|
||||
'path/in/repo/test.txt',
|
||||
'path/deeper/in/repo/test.txt'
|
||||
]
|
||||
const expected: string[] = [
|
||||
'/tmp/path/in/repo/test.txt',
|
||||
'/tmp/path/deeper/in/repo/test.txt'
|
||||
]
|
||||
const newFilePaths =
|
||||
manifestUpdateUtils.moveFilesToTmpDir(originalFilePaths)
|
||||
expect(newFilePaths).toEqual(expected)
|
||||
})
|
||||
})
|
||||
@@ -3,7 +3,7 @@ import * as fs from 'fs'
|
||||
import * as yaml from 'js-yaml'
|
||||
import * as path from 'path'
|
||||
import * as fileHelper from './fileUtils'
|
||||
import {getTempDirectory} from './fileUtils'
|
||||
import {moveFileToTmpDir} from './fileUtils'
|
||||
import {
|
||||
InputObjectKindNotDefinedError,
|
||||
InputObjectMetadataNotDefinedError,
|
||||
@@ -26,10 +26,14 @@ export function updateManifestFiles(manifestFilePaths: string[]) {
|
||||
throw new Error('Manifest files not provided')
|
||||
}
|
||||
|
||||
// move original set of input files to tmp dir
|
||||
const manifestFilesInTempDir = moveFilesToTmpDir(manifestFilePaths)
|
||||
|
||||
// update container images
|
||||
const containers: string[] = core.getInput('images').split('\n')
|
||||
|
||||
const manifestFiles = updateContainerImagesInManifestFiles(
|
||||
manifestFilePaths,
|
||||
manifestFilesInTempDir,
|
||||
containers
|
||||
)
|
||||
|
||||
@@ -41,6 +45,12 @@ export function updateManifestFiles(manifestFilePaths: string[]) {
|
||||
return updateImagePullSecretsInManifestFiles(manifestFiles, imagePullSecrets)
|
||||
}
|
||||
|
||||
export function moveFilesToTmpDir(filepaths: string[]): string[] {
|
||||
return filepaths.map((filename) => {
|
||||
return moveFileToTmpDir(filename)
|
||||
})
|
||||
}
|
||||
|
||||
export function UnsetClusterSpecificDetails(resource: any) {
|
||||
if (!resource) {
|
||||
return
|
||||
@@ -70,12 +80,9 @@ function updateContainerImagesInManifestFiles(
|
||||
): string[] {
|
||||
if (filePaths?.length <= 0) return filePaths
|
||||
|
||||
const newFilePaths = []
|
||||
|
||||
// update container images
|
||||
filePaths.forEach((filePath: string) => {
|
||||
let contents = fs.readFileSync(filePath).toString()
|
||||
|
||||
containers.forEach((container: string) => {
|
||||
let [imageName] = container.split(':')
|
||||
if (imageName.indexOf('@') > 0) {
|
||||
@@ -91,13 +98,10 @@ function updateContainerImagesInManifestFiles(
|
||||
})
|
||||
|
||||
// write updated files
|
||||
const tempDirectory = getTempDirectory()
|
||||
const fileName = path.join(tempDirectory, path.basename(filePath))
|
||||
fs.writeFileSync(path.join(fileName), contents)
|
||||
newFilePaths.push(fileName)
|
||||
fs.writeFileSync(path.join(filePath), contents)
|
||||
})
|
||||
|
||||
return newFilePaths
|
||||
return filePaths
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -7,7 +7,7 @@ def delete(kind, name, namespace):
|
||||
if (name == "all"):
|
||||
print('kubectl delete --all' + kind + ' -n ' + namespace)
|
||||
deletion = subprocess.Popen(
|
||||
['kubectl', 'delete', kind, name, '--namespace', namespace])
|
||||
['kubectl', 'delete', kind, '--all', '--namespace', namespace])
|
||||
result, err = deletion.communicate()
|
||||
else:
|
||||
print('kubectl delete ' + kind + ' ' + name + ' -n ' + namespace)
|
||||
@@ -21,7 +21,7 @@ def delete(kind, name, namespace):
|
||||
def main():
|
||||
kind = sys.argv[1]
|
||||
name = sys.argv[2]
|
||||
namespace = 'test-' + sys.argv[3]
|
||||
namespace = sys.argv[3]
|
||||
delete(kind, name, namespace)
|
||||
|
||||
|
||||
|
||||
@@ -41,10 +41,6 @@ def parseArgs(sysArgs):
|
||||
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(","), ":")
|
||||
@@ -60,6 +56,9 @@ def parseArgs(sysArgs):
|
||||
if ingressServicesKey in argsDict:
|
||||
argsDict[ingressServicesKey] = argsDict[ingressServicesKey].split(",")
|
||||
|
||||
if annotationsKey in argsDict:
|
||||
argsDict[annotationsKey] = argsDict[annotationsKey].split(",")
|
||||
|
||||
return argsDict
|
||||
|
||||
|
||||
@@ -98,14 +97,14 @@ def verifyDeployment(deployment, parsedArgs):
|
||||
return dictMatch, msg
|
||||
|
||||
if annotationsKey in parsedArgs:
|
||||
dictMatch, msg = compareDicts(
|
||||
deployment['metadata']['annotations'], parsedArgs[annotationsKey], annotationsKey)
|
||||
if not dictMatch:
|
||||
return dictMatch, msg
|
||||
|
||||
if len(parsedArgs[annotationsKey]) != len(deployment['metadata']['annotations']):
|
||||
return False, f"expected {len(parsedArgs[annotationsKey])} annotations but found {len(deployment['metadata']['annotations'])}"
|
||||
keysPresent, msg = validateKeyPresence(
|
||||
deployment['metadata']['annotations'], parsedArgs[annotationsKey])
|
||||
if not keysPresent:
|
||||
return keysPresent, msg
|
||||
return True, ""
|
||||
|
||||
|
||||
def verifyService(service, parsedArgs):
|
||||
# test selector labels, labels, annotations
|
||||
if not selectorLabelsKey in parsedArgs:
|
||||
@@ -124,10 +123,10 @@ def verifyService(service, parsedArgs):
|
||||
return dictMatch, msg
|
||||
|
||||
if annotationsKey in parsedArgs:
|
||||
dictMatch, msg = compareDicts(
|
||||
service['metadata']['annotations'], parsedArgs[annotationsKey], annotationsKey)
|
||||
if not dictMatch:
|
||||
return dictMatch, msg
|
||||
keysPresent, msg = validateKeyPresence(
|
||||
service['metadata']['annotations'], parsedArgs[annotationsKey])
|
||||
if not keysPresent:
|
||||
return keysPresent, msg
|
||||
|
||||
return True, ""
|
||||
|
||||
@@ -188,6 +187,13 @@ def compareDicts(actual: dict, expected: dict, paramName=""):
|
||||
|
||||
return True, ""
|
||||
|
||||
def validateKeyPresence(actualDict: dict, expectedKeys: list):
|
||||
actualKeys = actualDict.keys()
|
||||
for key in expectedKeys:
|
||||
if key not in actualKeys:
|
||||
return False, f"expected key {key} not found in actual dict. \n actual dict keys {','.join(actualKeys)}"
|
||||
|
||||
return True, ""
|
||||
|
||||
def main():
|
||||
parsedArgs: dict = parseArgs(sys.argv[1:])
|
||||
@@ -220,14 +226,13 @@ def main():
|
||||
|
||||
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()
|
||||
foundObjects = os.popen(getAllObjectsCmd).read()
|
||||
suffix = f"resources of type {kind}: {foundObjects}"
|
||||
sys.exit(msg + " " + suffix)
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment3
|
||||
labels:
|
||||
app: nginx3
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx3
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx3
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service3
|
||||
spec:
|
||||
selector:
|
||||
app: nginx3
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment2
|
||||
labels:
|
||||
app: nginx2
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx2
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx2
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service2
|
||||
spec:
|
||||
selector:
|
||||
app: nginx2
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
Reference in New Issue
Block a user