mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-06-25 05:59:26 +08:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e569e818b | |||
| adc1ea014e | |||
| 8898d95f4f | |||
| 33608d18f7 | |||
| acd12a4705 | |||
| 81557b8633 | |||
| e1b9842236 | |||
| 26cb2cdb5f | |||
| 5ebbfbbefe | |||
| ac49626466 | |||
| d332939666 | |||
| 2577009bcb | |||
| a58ad23e7f | |||
| ee4b5d33e0 | |||
| 625898f6eb | |||
| 8d257fed50 | |||
| 4f6b70e29a | |||
| 202bacc71b | |||
| 2c09684db9 | |||
| 282a81e1fc | |||
| ce7c8f066f | |||
| 25ded46b9d | |||
| 56e4abca5e | |||
| 4bd69f56a9 | |||
| 04921d7d06 | |||
| 51b95a5ca2 | |||
| 49257c6f33 | |||
| 895952654c | |||
| 0fd84a1b0d | |||
| d35174fe93 | |||
| f80ed6c460 | |||
| 72bc167726 | |||
| 21d3af2857 | |||
| a80355209a | |||
| 92589546e8 | |||
| b9146889f3 |
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Blue Green Bug Bash
|
||||||
|
about: Issues found in blue-green strategy bug bash
|
||||||
|
title: "[Blue-Green Bug Bash] "
|
||||||
|
labels: 'blue-green-bug-bash'
|
||||||
|
assignees: ajinkya599
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Repro steps:
|
||||||
|
- Add steps to repro the issue here
|
||||||
|
-
|
||||||
|
|
||||||
|
Current behaviour:
|
||||||
|
- What is the current behaviour?
|
||||||
|
-
|
||||||
|
|
||||||
|
Expected behaviour:
|
||||||
|
- What is the expected behaviour?
|
||||||
|
-
|
||||||
@@ -4,14 +4,14 @@ repository=$3
|
|||||||
prNumber=$4
|
prNumber=$4
|
||||||
frombranch=$5
|
frombranch=$5
|
||||||
tobranch=$6
|
tobranch=$6
|
||||||
|
patUser=$7
|
||||||
getPayLoad() {
|
getPayLoad() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
{
|
{
|
||||||
"event_type": "K8sDeployActionPR",
|
"event_type": "K8sDeployActionPR",
|
||||||
"client_payload":
|
"client_payload":
|
||||||
{
|
{
|
||||||
"action": "CreateSecret",
|
"action": "K8sDeploy",
|
||||||
"commit": "$commit",
|
"commit": "$commit",
|
||||||
"repository": "$repository",
|
"repository": "$repository",
|
||||||
"prNumber": "$prNumber",
|
"prNumber": "$prNumber",
|
||||||
@@ -22,11 +22,11 @@ getPayLoad() {
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
response=$(curl -X POST -H "Authorization: token $token" https://api.github.com/repos/Azure/azure-actions-integration-tests/dispatches --data "$(getPayLoad)")
|
response=$(curl -u $patUser:$token -X POST https://api.github.com/repos/Azure/azure-actions-integration-tests/dispatches --data "$(getPayLoad)")
|
||||||
|
|
||||||
if [ "$response" == "" ]; then
|
if [ "$response" == "" ]; then
|
||||||
echo "Integration tests triggered successfully"
|
echo "Integration tests triggered successfully"
|
||||||
else
|
else
|
||||||
echo "Triggering integration tests failed with: '$response'"
|
echo "Triggering integration tests failed with: '$response'"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
name: setting-default-labels
|
||||||
|
|
||||||
|
# Controls when the action will run.
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0/3 * * *"
|
||||||
|
|
||||||
|
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
# The type of runner that the job will run on
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- uses: actions/stale@v3
|
||||||
|
name: Setting issue as idle
|
||||||
|
with:
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
stale-issue-message: 'This issue is idle because it has been open for 14 days with no activity.'
|
||||||
|
stale-issue-label: 'idle'
|
||||||
|
days-before-stale: 14
|
||||||
|
days-before-close: -1
|
||||||
|
operations-per-run: 100
|
||||||
|
exempt-issue-labels: 'backlog'
|
||||||
|
|
||||||
|
- uses: actions/stale@v3
|
||||||
|
name: Setting PR as idle
|
||||||
|
with:
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
stale-pr-message: 'This PR is idle because it has been open for 14 days with no activity.'
|
||||||
|
stale-pr-label: 'idle'
|
||||||
|
days-before-stale: 14
|
||||||
|
days-before-close: -1
|
||||||
|
operations-per-run: 100
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
name: "Trigger Integration tests"
|
name: "Trigger Integration tests"
|
||||||
on:
|
on:
|
||||||
# push:
|
|
||||||
# branches:
|
|
||||||
# - master
|
|
||||||
# - 'releases/*'
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- main
|
||||||
- 'releases/*'
|
- 'releases/*'
|
||||||
jobs:
|
jobs:
|
||||||
trigger-integration-tests:
|
trigger-integration-tests:
|
||||||
@@ -20,4 +16,4 @@ jobs:
|
|||||||
|
|
||||||
- name: Trigger Test run
|
- name: Trigger Test run
|
||||||
run: |
|
run: |
|
||||||
bash ./IntegrationTests/.github/workflows/TriggerIntegrationTests.sh ${{ secrets.L2_REPO_TOKEN }} ${{ github.event.pull_request.head.sha }} ${{ github.repository }} ${{ github.event.pull_request.number }} ${{ github.event.pull_request.head.ref }} ${{ github.event.pull_request.base.ref }}
|
bash ./IntegrationTests/.github/workflows/TriggerIntegrationTests.sh ${{ secrets.L2_REPO_TOKEN }} ${{ github.event.pull_request.head.sha }} ${{ github.repository }} ${{ github.event.pull_request.number }} ${{ github.event.pull_request.head.ref }} ${{ github.event.pull_request.base.ref }} ${{ secrets.L2_REPO_USER }}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: "build-test"
|
name: "Run unit tests."
|
||||||
on: # rebuild any PRs and main branch changes
|
on: # rebuild any PRs and main branch changes
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
@@ -16,5 +16,4 @@ jobs:
|
|||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- run: |
|
- run: |
|
||||||
npm install
|
npm install
|
||||||
npm build
|
|
||||||
npm test
|
npm test
|
||||||
+1
-329
@@ -1,329 +1 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
node_modules
|
||||||
## files generated by popular Visual Studio add-ons.
|
|
||||||
##
|
|
||||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
|
||||||
|
|
||||||
# User-specific files
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userosscache
|
|
||||||
*.sln.docstates
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
|
||||||
*.userprefs
|
|
||||||
|
|
||||||
# Build results
|
|
||||||
[Dd]ebug/
|
|
||||||
[Dd]ebugPublic/
|
|
||||||
[Rr]elease/
|
|
||||||
[Rr]eleases/
|
|
||||||
x64/
|
|
||||||
x86/
|
|
||||||
bld/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
[Ll]og/
|
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
|
||||||
.vs/
|
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
|
||||||
#wwwroot/
|
|
||||||
|
|
||||||
# Visual Studio 2017 auto generated files
|
|
||||||
Generated\ Files/
|
|
||||||
|
|
||||||
# MSTest test Results
|
|
||||||
[Tt]est[Rr]esult*/
|
|
||||||
[Bb]uild[Ll]og.*
|
|
||||||
|
|
||||||
# NUNIT
|
|
||||||
*.VisualState.xml
|
|
||||||
TestResult.xml
|
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
|
||||||
[Dd]ebugPS/
|
|
||||||
[Rr]eleasePS/
|
|
||||||
dlldata.c
|
|
||||||
|
|
||||||
# Benchmark Results
|
|
||||||
BenchmarkDotNet.Artifacts/
|
|
||||||
|
|
||||||
# .NET Core
|
|
||||||
project.lock.json
|
|
||||||
project.fragment.lock.json
|
|
||||||
artifacts/
|
|
||||||
**/Properties/launchSettings.json
|
|
||||||
|
|
||||||
# StyleCop
|
|
||||||
StyleCopReport.xml
|
|
||||||
|
|
||||||
# Files built by Visual Studio
|
|
||||||
*_i.c
|
|
||||||
*_p.c
|
|
||||||
*_i.h
|
|
||||||
*.ilk
|
|
||||||
*.meta
|
|
||||||
*.obj
|
|
||||||
*.iobj
|
|
||||||
*.pch
|
|
||||||
*.pdb
|
|
||||||
*.ipdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*.log
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.svclog
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Chutzpah Test files
|
|
||||||
_Chutzpah*
|
|
||||||
|
|
||||||
# Visual C++ cache files
|
|
||||||
ipch/
|
|
||||||
*.aps
|
|
||||||
*.ncb
|
|
||||||
*.opendb
|
|
||||||
*.opensdf
|
|
||||||
*.sdf
|
|
||||||
*.cachefile
|
|
||||||
*.VC.db
|
|
||||||
*.VC.VC.opendb
|
|
||||||
|
|
||||||
# Visual Studio profiler
|
|
||||||
*.psess
|
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
*.sap
|
|
||||||
|
|
||||||
# Visual Studio Trace Files
|
|
||||||
*.e2e
|
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
|
||||||
$tf/
|
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
|
||||||
*.gpState
|
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
|
||||||
_ReSharper*/
|
|
||||||
*.[Rr]e[Ss]harper
|
|
||||||
*.DotSettings.user
|
|
||||||
|
|
||||||
# JustCode is a .NET coding add-in
|
|
||||||
.JustCode
|
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
|
||||||
_TeamCity*
|
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
|
||||||
*.dotCover
|
|
||||||
|
|
||||||
# AxoCover is a Code Coverage Tool
|
|
||||||
.axoCover/*
|
|
||||||
!.axoCover/settings.json
|
|
||||||
|
|
||||||
# Visual Studio code coverage results
|
|
||||||
*.coverage
|
|
||||||
*.coveragexml
|
|
||||||
|
|
||||||
# NCrunch
|
|
||||||
_NCrunch_*
|
|
||||||
.*crunch*.local.xml
|
|
||||||
nCrunchTemp_*
|
|
||||||
|
|
||||||
# MightyMoose
|
|
||||||
*.mm.*
|
|
||||||
AutoTest.Net/
|
|
||||||
|
|
||||||
# Web workbench (sass)
|
|
||||||
.sass-cache/
|
|
||||||
|
|
||||||
# Installshield output folder
|
|
||||||
[Ee]xpress/
|
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
|
||||||
DocProject/buildhelp/
|
|
||||||
DocProject/Help/*.HxT
|
|
||||||
DocProject/Help/*.HxC
|
|
||||||
DocProject/Help/*.hhc
|
|
||||||
DocProject/Help/*.hhk
|
|
||||||
DocProject/Help/*.hhp
|
|
||||||
DocProject/Help/Html2
|
|
||||||
DocProject/Help/html
|
|
||||||
|
|
||||||
# Click-Once directory
|
|
||||||
publish/
|
|
||||||
|
|
||||||
# Publish Web Output
|
|
||||||
*.[Pp]ublish.xml
|
|
||||||
*.azurePubxml
|
|
||||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
|
||||||
*.pubxml
|
|
||||||
*.publishproj
|
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
|
||||||
# in these scripts will be unencrypted
|
|
||||||
PublishScripts/
|
|
||||||
|
|
||||||
# NuGet Packages
|
|
||||||
*.nupkg
|
|
||||||
# The packages folder can be ignored because of Package Restore
|
|
||||||
**/[Pp]ackages/*
|
|
||||||
# except build/, which is used as an MSBuild target.
|
|
||||||
!**/[Pp]ackages/build/
|
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
|
||||||
#!**/[Pp]ackages/repositories.config
|
|
||||||
# NuGet v3's project.json files produces more ignorable files
|
|
||||||
*.nuget.props
|
|
||||||
*.nuget.targets
|
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
|
||||||
csx/
|
|
||||||
*.build.csdef
|
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
|
||||||
ecf/
|
|
||||||
rcf/
|
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
|
||||||
AppPackages/
|
|
||||||
BundleArtifacts/
|
|
||||||
Package.StoreAssociation.xml
|
|
||||||
_pkginfo.txt
|
|
||||||
*.appx
|
|
||||||
|
|
||||||
# Visual Studio cache files
|
|
||||||
# files ending in .cache can be ignored
|
|
||||||
*.[Cc]ache
|
|
||||||
# but keep track of directories ending in .cache
|
|
||||||
!*.[Cc]ache/
|
|
||||||
|
|
||||||
# Others
|
|
||||||
ClientBin/
|
|
||||||
~$*
|
|
||||||
*~
|
|
||||||
*.dbmdl
|
|
||||||
*.dbproj.schemaview
|
|
||||||
*.jfm
|
|
||||||
*.pfx
|
|
||||||
*.publishsettings
|
|
||||||
orleans.codegen.cs
|
|
||||||
|
|
||||||
# Including strong name files can present a security risk
|
|
||||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
|
||||||
#*.snk
|
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
|
||||||
#bower_components/
|
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
|
||||||
Generated_Code/
|
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
|
||||||
# because we have git ;-)
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
Backup*/
|
|
||||||
UpgradeLog*.XML
|
|
||||||
UpgradeLog*.htm
|
|
||||||
ServiceFabricBackup/
|
|
||||||
*.rptproj.bak
|
|
||||||
|
|
||||||
# SQL Server files
|
|
||||||
*.mdf
|
|
||||||
*.ldf
|
|
||||||
*.ndf
|
|
||||||
|
|
||||||
# Business Intelligence projects
|
|
||||||
*.rdl.data
|
|
||||||
*.bim.layout
|
|
||||||
*.bim_*.settings
|
|
||||||
*.rptproj.rsuser
|
|
||||||
|
|
||||||
# Microsoft Fakes
|
|
||||||
FakesAssemblies/
|
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
|
||||||
*.GhostDoc.xml
|
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
|
||||||
.ntvs_analysis.dat
|
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
|
||||||
*.plg
|
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
|
||||||
*.opt
|
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
|
||||||
*.vbw
|
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
|
||||||
**/*.Server/GeneratedArtifacts
|
|
||||||
**/*.Server/ModelManifest.xml
|
|
||||||
_Pvt_Extensions
|
|
||||||
|
|
||||||
# Paket dependency manager
|
|
||||||
.paket/paket.exe
|
|
||||||
paket-files/
|
|
||||||
|
|
||||||
# FAKE - F# Make
|
|
||||||
.fake/
|
|
||||||
|
|
||||||
# JetBrains Rider
|
|
||||||
.idea/
|
|
||||||
*.sln.iml
|
|
||||||
|
|
||||||
# CodeRush
|
|
||||||
.cr/
|
|
||||||
|
|
||||||
# Python Tools for Visual Studio (PTVS)
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Cake - Uncomment if you are using it
|
|
||||||
# tools/**
|
|
||||||
# !tools/packages.config
|
|
||||||
|
|
||||||
# Tabs Studio
|
|
||||||
*.tss
|
|
||||||
|
|
||||||
# Telerik's JustMock configuration file
|
|
||||||
*.jmconfig
|
|
||||||
|
|
||||||
# BizTalk build output
|
|
||||||
*.btp.cs
|
|
||||||
*.btm.cs
|
|
||||||
*.odx.cs
|
|
||||||
*.xsd.cs
|
|
||||||
|
|
||||||
# OpenCover UI analysis results
|
|
||||||
OpenCover/
|
|
||||||
|
|
||||||
# Azure Stream Analytics local run output
|
|
||||||
ASALocalRun/
|
|
||||||
|
|
||||||
# MSBuild Binary and Structured Log
|
|
||||||
*.binlog
|
|
||||||
|
|
||||||
# NVidia Nsight GPU debugger configuration file
|
|
||||||
*.nvuser
|
|
||||||
|
|
||||||
# MFractors (Xamarin productivity tool) working folder
|
|
||||||
.mfractor/
|
|
||||||
@@ -103,7 +103,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@v1
|
- uses: Azure/k8s-deploy@v1.4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
manifests: |
|
manifests: |
|
||||||
@@ -119,7 +119,7 @@ Following are the key capabilities of this action:
|
|||||||
### Deployment Strategies - Canary deployment without service mesh
|
### Deployment Strategies - Canary deployment without service mesh
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: Azure/k8s-deploy@v1
|
- uses: Azure/k8s-deploy@v1.4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||||
@@ -136,7 +136,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@v1
|
- uses: Azure/k8s-deploy@v1.4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||||
@@ -153,7 +153,7 @@ Following are the key capabilities of this action:
|
|||||||
### Deployment Strategies - Canary deployment based on Service Mesh Interface
|
### Deployment Strategies - Canary deployment based on Service Mesh Interface
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: Azure/k8s-deploy@v1
|
- uses: Azure/k8s-deploy@v1.4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||||
@@ -170,7 +170,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@v1
|
- uses: Azure/k8s-deploy@v1.4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }} '
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }} '
|
||||||
@@ -187,7 +187,7 @@ Following are the key capabilities of this action:
|
|||||||
### Deployment Strategies - Blue-Green deployment with different route methods
|
### Deployment Strategies - Blue-Green deployment with different route methods
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: Azure/k8s-deploy@v1
|
- uses: Azure/k8s-deploy@v1.4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||||
@@ -206,7 +206,7 @@ Following are the key capabilities of this action:
|
|||||||
### **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@v1
|
- uses: Azure/k8s-deploy@v1.4
|
||||||
with:
|
with:
|
||||||
namespace: 'myapp'
|
namespace: 'myapp'
|
||||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||||
@@ -218,7 +218,7 @@ Following are the key capabilities of this action:
|
|||||||
service.yaml
|
service.yaml
|
||||||
ingress-yml
|
ingress-yml
|
||||||
strategy: blue-green
|
strategy: blue-green
|
||||||
strategy: ingress # should be the same as the value when action was deploy
|
route-method: ingress # should be the same as the value when action was deploy
|
||||||
action: promote # substitute reject if you want to reject
|
action: promote # substitute reject if you want to reject
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -261,7 +261,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@v1
|
- uses: Azure/k8s-deploy@v1.4
|
||||||
with:
|
with:
|
||||||
manifests: |
|
manifests: |
|
||||||
manifests/deployment.yml
|
manifests/deployment.yml
|
||||||
@@ -304,7 +304,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@v1
|
- uses: Azure/k8s-deploy@v1.4
|
||||||
with:
|
with:
|
||||||
manifests: |
|
manifests: |
|
||||||
manifests/deployment.yml
|
manifests/deployment.yml
|
||||||
@@ -314,6 +314,138 @@ jobs:
|
|||||||
imagepullsecrets: |
|
imagepullsecrets: |
|
||||||
demo-k8s-secret
|
demo-k8s-secret
|
||||||
```
|
```
|
||||||
|
## Sample workflows for new traceability fields support
|
||||||
|
|
||||||
|
- Environment variable `HELM_CHART_PATHS` is a list of helmchart files expected by k8s-deploy - it will be populated automatically if you are using `k8s-bake` to generate the manifests.
|
||||||
|
- Use script to build image and add `dockerfile-path` label to it.
|
||||||
|
The value expected is the link to the dockerfile : `https://github.com/${{github.repo}}/blob/${{github.sha}}/Dockerfile`
|
||||||
|
If your dockerfile is in the same repo and branch where the workflow is run, it can be a relative path and it will be converted to a link for traceability.
|
||||||
|
- Run docker login action for each image registry - in case image build and image deploy are 2 distinct jobs in the same or separate workflows.
|
||||||
|
|
||||||
|
### End to end workflow for building and deploying container images
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on: [push]
|
||||||
|
env:
|
||||||
|
NAMESPACE: demo-ns2
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
|
- uses: Azure/docker-login@v1
|
||||||
|
with:
|
||||||
|
login-server: contoso.azurecr.io
|
||||||
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }} --label dockerfile-path=./Dockerfile
|
||||||
|
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||||
|
|
||||||
|
# Set the target AKS cluster.
|
||||||
|
- uses: Azure/aks-set-context@v1
|
||||||
|
with:
|
||||||
|
creds: '${{ secrets.AZURE_CREDENTIALS }}'
|
||||||
|
cluster-name: contoso
|
||||||
|
resource-group: contoso-rg
|
||||||
|
|
||||||
|
- uses: Azure/k8s-create-secret@v1
|
||||||
|
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@v1.2
|
||||||
|
with:
|
||||||
|
manifests: |
|
||||||
|
manifests/deployment.yml
|
||||||
|
manifests/service.yml
|
||||||
|
images: |
|
||||||
|
contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||||
|
imagepullsecrets: |
|
||||||
|
demo-k8s-secret
|
||||||
|
```
|
||||||
|
|
||||||
|
### CI workflow to build image and add `dockerfile-path` label to it. This image can then be used in another CD workflow.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on: [push]
|
||||||
|
env:
|
||||||
|
NAMESPACE: demo-ns2
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
|
- uses: Azure/docker-login@v1
|
||||||
|
with:
|
||||||
|
login-server: contoso.azurecr.io
|
||||||
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }} --label dockerfile-path=https://github.com/${{github.repo}}/blob/${{github.sha}}/Dockerfile
|
||||||
|
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### CD workflow using bake action to get manifests deploying to a Kubernetes cluster
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on: [push]
|
||||||
|
env:
|
||||||
|
NAMESPACE: demo-ns2
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
|
- uses: Azure/docker-login@v1
|
||||||
|
with:
|
||||||
|
login-server: contoso.azurecr.io
|
||||||
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
# Set the target AKS cluster.
|
||||||
|
- uses: Azure/aks-set-context@v1
|
||||||
|
with:
|
||||||
|
creds: '${{ secrets.AZURE_CREDENTIALS }}'
|
||||||
|
cluster-name: contoso
|
||||||
|
resource-group: contoso-rg
|
||||||
|
|
||||||
|
- uses: Azure/k8s-create-secret@v1
|
||||||
|
with:
|
||||||
|
namespace: ${{ env.NAMESPACE }}
|
||||||
|
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-bake@v1
|
||||||
|
with:
|
||||||
|
renderEngine: 'helm'
|
||||||
|
helmChart: './aks-helloworld/'
|
||||||
|
overrideFiles: './aks-helloworld/values-override.yaml'
|
||||||
|
overrides: |
|
||||||
|
replicas:2
|
||||||
|
helm-version: 'latest'
|
||||||
|
id: bake
|
||||||
|
|
||||||
|
- uses: Azure/k8s-deploy@v1.2
|
||||||
|
with:
|
||||||
|
manifests: ${{ steps.bake.outputs.manifestsBundle }}
|
||||||
|
images: |
|
||||||
|
contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||||
|
imagepullsecrets: |
|
||||||
|
demo-k8s-secret
|
||||||
|
```
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
@@ -327,4 +459,4 @@ provided by the bot. You will only need to do this once across all repos using o
|
|||||||
|
|
||||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||||
|
|||||||
+217
-22
@@ -6,11 +6,14 @@ import * as deployment from '../src/utilities/strategy-helpers/deployment-helper
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as io from '@actions/io';
|
import * as io from '@actions/io';
|
||||||
import * as toolCache from '@actions/tool-cache';
|
import * as toolCache from '@actions/tool-cache';
|
||||||
|
import * as util from 'util';
|
||||||
import * as fileHelper from '../src/utilities/files-helper';
|
import * as fileHelper from '../src/utilities/files-helper';
|
||||||
import { workflowAnnotations } from '../src/constants';
|
import { getWorkflowAnnotationKeyLabel, getWorkflowAnnotationsJson } from '../src/constants';
|
||||||
import * as inputParam from '../src/input-parameters';
|
import * as inputParam from '../src/input-parameters';
|
||||||
|
|
||||||
import { Kubectl, Resource } from '../src/kubectl-object-model';
|
import { Kubectl, Resource } from '../src/kubectl-object-model';
|
||||||
|
import * as httpClient from '../src/utilities/httpClient';
|
||||||
|
import * as utility from '../src/utilities/utility';
|
||||||
|
|
||||||
import { getkubectlDownloadURL } from "../src/utilities/kubectl-util";
|
import { getkubectlDownloadURL } from "../src/utilities/kubectl-util";
|
||||||
import { mocked } from 'ts-jest/utils';
|
import { mocked } from 'ts-jest/utils';
|
||||||
@@ -21,6 +24,7 @@ const os = require("os");
|
|||||||
const coreMock = mocked(core, true);
|
const coreMock = mocked(core, true);
|
||||||
const ioMock = mocked(io, true);
|
const ioMock = mocked(io, true);
|
||||||
const inputParamMock = mocked(inputParam, true);
|
const inputParamMock = mocked(inputParam, true);
|
||||||
|
const osMock = mocked(os, true);
|
||||||
|
|
||||||
const toolCacheMock = mocked(toolCache, true);
|
const toolCacheMock = mocked(toolCache, true);
|
||||||
const fileUtility = mocked(fs, true);
|
const fileUtility = mocked(fs, true);
|
||||||
@@ -36,14 +40,48 @@ const getAllPodsMock = {
|
|||||||
|
|
||||||
const getNamespaceMock = {
|
const getNamespaceMock = {
|
||||||
'code': 0,
|
'code': 0,
|
||||||
'stdout': '{"apiVersion": "v1","kind": "Namespace","metadata": {"annotations": {"workflow": ".github/workflows/workflow.yml","runUri": "https://github.com/testRepo/actions/runs/12345"}},"spec": {"finalizers": ["kubernetes"]},"status": {"phase": "Active"}}'
|
'stdout': '{"apiVersion": "v1","kind": "Namespace","metadata": {"annotations": {"githubWorkflow_c11401b9d232942bac19cbc5bc32b42d": "{\'run\': \'202489005\',\'repository\': \'testUser/hello-kubernetes\',\'workflow\': \'workflow1\',\'jobName\': \'build-and-deploy\',\'createdBy\': \'testUser\',\'runUri\': \'https://github.com/testUser/hello-kubernetes/actions/runs/202489005\',\'commit\': \'currentCommit\',\'lastSuccessRunCommit\': \'lastCommit\',\'branch\': \'refs/heads/branch-rename\',\'deployTimestamp\': \'1597062957973\',\'dockerfilePaths\': \'{}\',\'manifestsPaths\': \'[]\',\'helmChartPaths\': \'[]\',\'provider\': \'GitHub\'}","githubWorkflow_21fd7a597282ca5adc05ba99018b3706": "{\'run\': \'202504411\',\'repository\': \'testUser/hello-kubernetes\',\'workflow\': \'workflowMaster\',\'jobName\': \'build-and-deploy\',\'createdBy\': \'testUser\',\'runUri\': \'https://github.com/testUser/hello-kubernetes/actions/runs/202504411\',\'commit\': \'currentCommit1\',\'lastSuccessRunCommit\': \'NA\',\'branch\': \'refs/heads/master\',\'deployTimestamp\': \'1597063919873\',\'dockerfilePaths\': \'{}\',\'manifestsPaths\': \'[]\',\'helmChartPaths\': \'[]\',\'provider\': \'GitHub\'}"}},"spec": {"finalizers": ["kubernetes"]},"status": {"phase": "Active"}}'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getWorkflowsUrlResponse = {
|
||||||
|
'statusCode': httpClient.StatusCodes.OK,
|
||||||
|
'body': {
|
||||||
|
"total_count": 2,
|
||||||
|
"workflows": [
|
||||||
|
{
|
||||||
|
"id": 1477727,
|
||||||
|
"node_id": "MDg6V29ya2Zsb3cxNDYwNzI3",
|
||||||
|
"name": ".github/workflows/workflow.yml",
|
||||||
|
"path": ".github/workflows/workflow.yml",
|
||||||
|
"state": "active",
|
||||||
|
"created_at": "2020-06-03T23:41:06.000+05:30",
|
||||||
|
"updated_at": "2020-08-07T15:46:42.000+05:30",
|
||||||
|
"url": "https://api.github.com/repos/testUser/hello-kubernetes/actions/workflows/1460727",
|
||||||
|
"html_url": "https://github.com/testUser/hello-kubernetes/blob/master/.github/workflows/workflow.yml",
|
||||||
|
"badge_url": "https://github.com/testUser/hello-kubernetes/workflows/.github/workflows/workflow.yml/badge.svg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1532230,
|
||||||
|
"node_id": "MDg6V29ya2Zsb3cxNTMyMzMw",
|
||||||
|
"name": "NewWorkflow",
|
||||||
|
"path": ".github/workflows/workflow1.yml",
|
||||||
|
"state": "active",
|
||||||
|
"created_at": "2020-06-11T16:05:23.000+05:30",
|
||||||
|
"updated_at": "2020-08-07T15:46:42.000+05:30",
|
||||||
|
"url": "https://api.github.com/repos/testUser/hello-kubernetes/actions/workflows/1532330",
|
||||||
|
"html_url": "https://github.com/testUser/hello-kubernetes/blob/master/.github/workflows/workflowNew.yml",
|
||||||
|
"badge_url": "https://github.com/testUser/hello-kubernetes/workflows/KoDeyi/badge.svg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} as httpClient.WebResponse;
|
||||||
|
|
||||||
const resources: Resource[] = [{ type: "Deployment", name: "AppName" }];
|
const resources: Resource[] = [{ type: "Deployment", name: "AppName" }];
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
deploymentYaml = fs.readFileSync(path.join(__dirname, 'manifests', 'deployment.yml'), 'utf8');
|
deploymentYaml = fs.readFileSync(path.join(__dirname, 'manifests', 'deployment.yml'), 'utf8');
|
||||||
|
jest.spyOn(Date, 'now').mockImplementation(() => 1234561234567);
|
||||||
|
|
||||||
process.env["KUBECONFIG"] = 'kubeConfig';
|
process.env["KUBECONFIG"] = 'kubeConfig';
|
||||||
process.env['GITHUB_RUN_ID'] = '12345';
|
process.env['GITHUB_RUN_ID'] = '12345';
|
||||||
process.env['GITHUB_WORKFLOW'] = '.github/workflows/workflow.yml';
|
process.env['GITHUB_WORKFLOW'] = '.github/workflows/workflow.yml';
|
||||||
@@ -52,12 +90,18 @@ beforeEach(() => {
|
|||||||
process.env['GITHUB_REPOSITORY'] = 'testRepo';
|
process.env['GITHUB_REPOSITORY'] = 'testRepo';
|
||||||
process.env['GITHUB_SHA'] = 'testCommit';
|
process.env['GITHUB_SHA'] = 'testCommit';
|
||||||
process.env['GITHUB_REF'] = 'testBranch';
|
process.env['GITHUB_REF'] = 'testBranch';
|
||||||
|
process.env['GITHUB_TOKEN'] = 'testToken';
|
||||||
})
|
})
|
||||||
|
|
||||||
test("setKubectlPath() - install a particular version", async () => {
|
test.each([
|
||||||
|
['arm', 'arm'],
|
||||||
|
['arm64', 'arm64'],
|
||||||
|
['x64', 'amd64']
|
||||||
|
])("setKubectlPath() - install a particular version on %s", async (osArch, kubectlArch) => {
|
||||||
const kubectlVersion = 'v1.18.0'
|
const kubectlVersion = 'v1.18.0'
|
||||||
//Mocks
|
//Mocks
|
||||||
coreMock.getInput = jest.fn().mockReturnValue(kubectlVersion);
|
coreMock.getInput = jest.fn().mockReturnValue(kubectlVersion);
|
||||||
|
osMock.arch = jest.fn().mockReturnValue(osArch);
|
||||||
toolCacheMock.find = jest.fn().mockReturnValue(undefined);
|
toolCacheMock.find = jest.fn().mockReturnValue(undefined);
|
||||||
toolCacheMock.downloadTool = jest.fn().mockReturnValue('downloadpath');
|
toolCacheMock.downloadTool = jest.fn().mockReturnValue('downloadpath');
|
||||||
toolCacheMock.cacheFile = jest.fn().mockReturnValue('cachepath');
|
toolCacheMock.cacheFile = jest.fn().mockReturnValue('cachepath');
|
||||||
@@ -66,7 +110,7 @@ test("setKubectlPath() - install a particular version", async () => {
|
|||||||
//Invoke and assert
|
//Invoke and assert
|
||||||
await expect(action.run()).resolves.not.toThrow();
|
await expect(action.run()).resolves.not.toThrow();
|
||||||
expect(toolCacheMock.find).toBeCalledWith('kubectl', kubectlVersion);
|
expect(toolCacheMock.find).toBeCalledWith('kubectl', kubectlVersion);
|
||||||
expect(toolCacheMock.downloadTool).toBeCalledWith(getkubectlDownloadURL(kubectlVersion));
|
expect(toolCacheMock.downloadTool).toBeCalledWith(getkubectlDownloadURL(kubectlVersion, kubectlArch));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("setKubectlPath() - install a latest version", async () => {
|
test("setKubectlPath() - install a latest version", async () => {
|
||||||
@@ -166,6 +210,111 @@ test("run() - deploy - Manifiest not provided", async () => {
|
|||||||
expect(coreMock.setFailed).toBeCalledWith('No manifests supplied to deploy');
|
expect(coreMock.setFailed).toBeCalledWith('No manifests supplied to deploy');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("run() - deploy - Only one manifest with no delimiters", async () => {
|
||||||
|
const kubectlVersion = 'v1.18.0'
|
||||||
|
coreMock.getInput = jest.fn().mockImplementation((name) => {
|
||||||
|
if (name == 'manifests') {
|
||||||
|
return "bg-smi.yml";
|
||||||
|
}
|
||||||
|
if (name == 'action') {
|
||||||
|
return 'deploy';
|
||||||
|
}
|
||||||
|
return kubectlVersion;
|
||||||
|
});
|
||||||
|
coreMock.setFailed = jest.fn();
|
||||||
|
toolCacheMock.find = jest.fn().mockReturnValue(undefined);
|
||||||
|
toolCacheMock.downloadTool = jest.fn().mockReturnValue('downloadpath');
|
||||||
|
toolCacheMock.cacheFile = jest.fn().mockReturnValue('cachepath');
|
||||||
|
fileUtility.chmodSync = jest.fn();
|
||||||
|
|
||||||
|
//Invoke and assert
|
||||||
|
await expect(action.run()).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("run() - deploy - Manifests provided by new line delimiter", async () => {
|
||||||
|
const kubectlVersion = 'v1.18.0'
|
||||||
|
coreMock.getInput = jest.fn().mockImplementation((name) => {
|
||||||
|
if (name == 'manifests') {
|
||||||
|
return "bg-smi.yml\n bg.yml\ndeployment.yml";
|
||||||
|
}
|
||||||
|
if (name == 'action') {
|
||||||
|
return 'deploy';
|
||||||
|
}
|
||||||
|
return kubectlVersion;
|
||||||
|
});
|
||||||
|
coreMock.setFailed = jest.fn();
|
||||||
|
toolCacheMock.find = jest.fn().mockReturnValue(undefined);
|
||||||
|
toolCacheMock.downloadTool = jest.fn().mockReturnValue('downloadpath');
|
||||||
|
toolCacheMock.cacheFile = jest.fn().mockReturnValue('cachepath');
|
||||||
|
fileUtility.chmodSync = jest.fn();
|
||||||
|
|
||||||
|
//Invoke and assert
|
||||||
|
await expect(action.run()).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("run() - deploy - Manifests provided by comma as a delimiter", async () => {
|
||||||
|
const kubectlVersion = 'v1.18.0'
|
||||||
|
coreMock.getInput = jest.fn().mockImplementation((name) => {
|
||||||
|
if (name == 'manifests') {
|
||||||
|
return "bg-smi.yml, bg.yml, deployment.yml";
|
||||||
|
}
|
||||||
|
if (name == 'action') {
|
||||||
|
return 'deploy';
|
||||||
|
}
|
||||||
|
return kubectlVersion;
|
||||||
|
});
|
||||||
|
coreMock.setFailed = jest.fn();
|
||||||
|
toolCacheMock.find = jest.fn().mockReturnValue(undefined);
|
||||||
|
toolCacheMock.downloadTool = jest.fn().mockReturnValue('downloadpath');
|
||||||
|
toolCacheMock.cacheFile = jest.fn().mockReturnValue('cachepath');
|
||||||
|
fileUtility.chmodSync = jest.fn();
|
||||||
|
|
||||||
|
//Invoke and assert
|
||||||
|
await expect(action.run()).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("run() - deploy - Manifests provided by both new line and comma as a delimiter", async () => {
|
||||||
|
const kubectlVersion = 'v1.18.0'
|
||||||
|
coreMock.getInput = jest.fn().mockImplementation((name) => {
|
||||||
|
if (name == 'manifests') {
|
||||||
|
return "bg-smi.yml\nbg.yml,deployment.yml";
|
||||||
|
}
|
||||||
|
if (name == 'action') {
|
||||||
|
return 'deploy';
|
||||||
|
}
|
||||||
|
return kubectlVersion;
|
||||||
|
});
|
||||||
|
coreMock.setFailed = jest.fn();
|
||||||
|
toolCacheMock.find = jest.fn().mockReturnValue(undefined);
|
||||||
|
toolCacheMock.downloadTool = jest.fn().mockReturnValue('downloadpath');
|
||||||
|
toolCacheMock.cacheFile = jest.fn().mockReturnValue('cachepath');
|
||||||
|
fileUtility.chmodSync = jest.fn();
|
||||||
|
|
||||||
|
//Invoke and assert
|
||||||
|
await expect(action.run()).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("run() - deploy - Manifests provided by both new line and comma and semi-colon as a delimiter", async () => {
|
||||||
|
const kubectlVersion = 'v1.18.0'
|
||||||
|
coreMock.getInput = jest.fn().mockImplementation((name) => {
|
||||||
|
if (name == 'manifests') {
|
||||||
|
return "bg-smi.yml\nbg.yml,deployment.yml;bg.yml";
|
||||||
|
}
|
||||||
|
if (name == 'action') {
|
||||||
|
return 'deploy';
|
||||||
|
}
|
||||||
|
return kubectlVersion;
|
||||||
|
});
|
||||||
|
coreMock.setFailed = jest.fn();
|
||||||
|
toolCacheMock.find = jest.fn().mockReturnValue(undefined);
|
||||||
|
toolCacheMock.downloadTool = jest.fn().mockReturnValue('downloadpath');
|
||||||
|
toolCacheMock.cacheFile = jest.fn().mockReturnValue('cachepath');
|
||||||
|
fileUtility.chmodSync = jest.fn();
|
||||||
|
|
||||||
|
//Invoke and assert
|
||||||
|
await expect(action.run()).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
test("deployment - deploy() - Invokes with no manifestfiles", async () => {
|
test("deployment - deploy() - Invokes with no manifestfiles", async () => {
|
||||||
const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any;
|
const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any;
|
||||||
|
|
||||||
@@ -213,9 +362,11 @@ test("deployment - deploy() - Invokes with manifestfiles", async () => {
|
|||||||
kubeCtl.describe = jest.fn().mockReturnValue("");
|
kubeCtl.describe = jest.fn().mockReturnValue("");
|
||||||
kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
|
kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
|
||||||
kubeCtl.annotate = jest.fn().mockReturnValue("");
|
kubeCtl.annotate = jest.fn().mockReturnValue("");
|
||||||
|
kubeCtl.labelFiles = jest.fn().mockReturnValue("");
|
||||||
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
||||||
|
|
||||||
const readFileSpy = jest.spyOn(fs, 'readFileSync').mockImplementation(() => deploymentYaml);
|
const readFileSpy = jest.spyOn(fs, 'readFileSync').mockImplementation(() => deploymentYaml);
|
||||||
|
jest.spyOn(httpClient, 'sendRequest').mockImplementation(() => Promise.resolve(getWorkflowsUrlResponse));
|
||||||
|
|
||||||
//Invoke and assert
|
//Invoke and assert
|
||||||
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
|
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
|
||||||
@@ -241,9 +392,11 @@ test("deployment - deploy() - deploy force flag on", async () => {
|
|||||||
kubeCtl.describe = jest.fn().mockReturnValue("");
|
kubeCtl.describe = jest.fn().mockReturnValue("");
|
||||||
kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
|
kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
|
||||||
kubeCtl.annotate = jest.fn().mockReturnValue("");
|
kubeCtl.annotate = jest.fn().mockReturnValue("");
|
||||||
|
kubeCtl.labelFiles = jest.fn().mockReturnValue("");
|
||||||
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
||||||
|
|
||||||
const deploySpy = jest.spyOn(kubeCtl, 'apply').mockImplementation(() => applyResMock);
|
const deploySpy = jest.spyOn(kubeCtl, 'apply').mockImplementation(() => applyResMock);
|
||||||
|
jest.spyOn(httpClient, 'sendRequest').mockImplementation(() => Promise.resolve(getWorkflowsUrlResponse));
|
||||||
|
|
||||||
//Invoke and assert
|
//Invoke and assert
|
||||||
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
|
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
|
||||||
@@ -251,15 +404,20 @@ test("deployment - deploy() - deploy force flag on", async () => {
|
|||||||
deploySpy.mockRestore();
|
deploySpy.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("deployment - deploy() - Annotate resources", async () => {
|
test("deployment - deploy() - Annotate & label resources", async () => {
|
||||||
|
let deploymentConfig: utility.DeploymentConfig = { manifestFilePaths: ['manifests/deployment.yaml'], helmChartFilePaths: [], dockerfilePaths: {} };
|
||||||
|
let annotationKeyValStr = getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW) + '=' + getWorkflowAnnotationsJson('currentCommit', '.github/workflows/workflow.yml', deploymentConfig);
|
||||||
const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true);
|
const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true);
|
||||||
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
||||||
const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true);
|
const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true);
|
||||||
KubernetesObjectUtilityMock.getResources = jest.fn().mockReturnValue(resources);
|
KubernetesObjectUtilityMock.getResources = jest.fn().mockReturnValue(resources);
|
||||||
const fileHelperMock = mocked(fileHelper, true);
|
const fileHelperMock = mocked(fileHelper, true);
|
||||||
const fsMock = (mocked(fs, true));
|
const fsMock = (mocked(fs, true));
|
||||||
fileHelperMock.getTempDirectory = jest.fn().mockReturnValue("Local/Temp/");
|
fileHelperMock.getTempDirectory = jest.fn().mockReturnValue("~/Deployment_testapp_currentTimestamp");
|
||||||
fsMock.writeFileSync =jest.fn().mockReturnValue("");
|
fsMock.writeFileSync = jest.fn().mockReturnValue("");
|
||||||
|
jest.spyOn(utility, 'getWorkflowFilePath').mockImplementation(() => Promise.resolve(process.env.GITHUB_WORKFLOW));
|
||||||
|
jest.spyOn(utility, 'getDeploymentConfig').mockImplementation(() => Promise.resolve(deploymentConfig));
|
||||||
|
|
||||||
const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any;
|
const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any;
|
||||||
kubeCtl.apply = jest.fn().mockReturnValue("");
|
kubeCtl.apply = jest.fn().mockReturnValue("");
|
||||||
kubeCtl.getResource = jest.fn().mockReturnValue(getNamespaceMock);
|
kubeCtl.getResource = jest.fn().mockReturnValue(getNamespaceMock);
|
||||||
@@ -267,23 +425,31 @@ test("deployment - deploy() - Annotate resources", async () => {
|
|||||||
kubeCtl.getNewReplicaSet = jest.fn().mockReturnValue("testpod-776cbc86f9");
|
kubeCtl.getNewReplicaSet = jest.fn().mockReturnValue("testpod-776cbc86f9");
|
||||||
kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
|
kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
|
||||||
kubeCtl.annotate = jest.fn().mockReturnValue("");
|
kubeCtl.annotate = jest.fn().mockReturnValue("");
|
||||||
|
kubeCtl.labelFiles = jest.fn();
|
||||||
//Invoke and assert
|
//Invoke and assert
|
||||||
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
|
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
|
||||||
expect(kubeCtl.annotateFiles).toBeCalledWith(["Local/Temp/deployment.yaml"], workflowAnnotations, true);
|
expect(kubeCtl.annotate).toHaveBeenNthCalledWith(1, 'namespace', 'default', annotationKeyValStr);
|
||||||
|
expect(kubeCtl.annotateFiles).toBeCalledWith(["~/Deployment_testapp_currentTimestamp/deployment.yaml"], annotationKeyValStr);
|
||||||
expect(kubeCtl.annotate).toBeCalledTimes(2);
|
expect(kubeCtl.annotate).toBeCalledTimes(2);
|
||||||
|
expect(kubeCtl.labelFiles).toBeCalledWith(["~/Deployment_testapp_currentTimestamp/deployment.yaml"],
|
||||||
|
[`workflowFriendlyName=workflow.yml`, `workflow=${getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW)}`]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("deployment - deploy() - Skip Annotate namespace", async () => {
|
test("deployment - deploy() - Annotate & label resources for a new workflow", async () => {
|
||||||
process.env['GITHUB_REPOSITORY'] = 'test1Repo';
|
process.env.GITHUB_WORKFLOW = '.github/workflows/New Workflow.yml';
|
||||||
|
let deploymentConfig: utility.DeploymentConfig = { manifestFilePaths: ['manifests/deployment.yaml'], helmChartFilePaths: [], dockerfilePaths: {} }
|
||||||
|
let annotationKeyValStr = getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW) + '=' + getWorkflowAnnotationsJson('NA', '.github/workflows/New Workflow.yml', deploymentConfig);
|
||||||
const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true);
|
const KubernetesManifestUtilityMock = mocked(KubernetesManifestUtility, true);
|
||||||
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
||||||
const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true);
|
const KubernetesObjectUtilityMock = mocked(KubernetesObjectUtility, true);
|
||||||
KubernetesObjectUtilityMock.getResources = jest.fn().mockReturnValue(resources);
|
KubernetesObjectUtilityMock.getResources = jest.fn().mockReturnValue(resources);
|
||||||
const fileHelperMock = mocked(fileHelper, true);
|
const fileHelperMock = mocked(fileHelper, true);
|
||||||
const fsMock = (mocked(fs, true));
|
const fsMock = (mocked(fs, true));
|
||||||
fileHelperMock.getTempDirectory = jest.fn().mockReturnValue("Local/Temp/");
|
fileHelperMock.getTempDirectory = jest.fn().mockReturnValue("~/Deployment_testapp_currentTimestamp");
|
||||||
fsMock.writeFileSync =jest.fn().mockReturnValue("");
|
fsMock.writeFileSync = jest.fn().mockReturnValue("");
|
||||||
|
jest.spyOn(httpClient, 'sendRequest').mockImplementation(() => Promise.resolve(getWorkflowsUrlResponse));
|
||||||
|
jest.spyOn(utility, 'getDeploymentConfig').mockImplementation(() => Promise.resolve(deploymentConfig));
|
||||||
|
|
||||||
const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any;
|
const kubeCtl: jest.Mocked<Kubectl> = new Kubectl("") as any;
|
||||||
kubeCtl.apply = jest.fn().mockReturnValue("");
|
kubeCtl.apply = jest.fn().mockReturnValue("");
|
||||||
kubeCtl.getResource = jest.fn().mockReturnValue(getNamespaceMock);
|
kubeCtl.getResource = jest.fn().mockReturnValue(getNamespaceMock);
|
||||||
@@ -291,14 +457,14 @@ test("deployment - deploy() - Skip Annotate namespace", async () => {
|
|||||||
kubeCtl.getNewReplicaSet = jest.fn().mockReturnValue("testpod-776cbc86f9");
|
kubeCtl.getNewReplicaSet = jest.fn().mockReturnValue("testpod-776cbc86f9");
|
||||||
kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
|
kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
|
||||||
kubeCtl.annotate = jest.fn().mockReturnValue("");
|
kubeCtl.annotate = jest.fn().mockReturnValue("");
|
||||||
|
kubeCtl.labelFiles = jest.fn();
|
||||||
const consoleOutputSpy = jest.spyOn(process.stdout, "write").mockImplementation();
|
|
||||||
|
|
||||||
//Invoke and assert
|
//Invoke and assert
|
||||||
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
|
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
|
||||||
expect(kubeCtl.annotateFiles).toBeCalledWith(["Local/Temp/deployment.yaml"], workflowAnnotations, true);
|
expect(kubeCtl.annotate).toHaveBeenNthCalledWith(1, 'namespace', 'default', annotationKeyValStr);
|
||||||
expect(kubeCtl.annotate).toBeCalledTimes(1);
|
expect(kubeCtl.annotateFiles).toBeCalledWith(["~/Deployment_testapp_currentTimestamp/deployment.yaml"], annotationKeyValStr);
|
||||||
expect(consoleOutputSpy).toHaveBeenNthCalledWith(1, `##[debug]Skipping 'annotate namespace' as namespace annotated by other workflow` + os.EOL)
|
expect(kubeCtl.annotate).toBeCalledTimes(2);
|
||||||
|
expect(kubeCtl.labelFiles).toBeCalledWith(["~/Deployment_testapp_currentTimestamp/deployment.yaml"],
|
||||||
|
[`workflowFriendlyName=New_Workflow.yml`, `workflow=${getWorkflowAnnotationKeyLabel(process.env.GITHUB_WORKFLOW)}`]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("deployment - deploy() - Annotate resources failed", async () => {
|
test("deployment - deploy() - Annotate resources failed", async () => {
|
||||||
@@ -320,10 +486,39 @@ test("deployment - deploy() - Annotate resources failed", async () => {
|
|||||||
kubeCtl.describe = jest.fn().mockReturnValue("");
|
kubeCtl.describe = jest.fn().mockReturnValue("");
|
||||||
kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
|
kubeCtl.annotateFiles = jest.fn().mockReturnValue("");
|
||||||
kubeCtl.annotate = jest.fn().mockReturnValue(annotateMock);
|
kubeCtl.annotate = jest.fn().mockReturnValue(annotateMock);
|
||||||
|
kubeCtl.labelFiles = jest.fn().mockReturnValue("");
|
||||||
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
KubernetesManifestUtilityMock.checkManifestStability = jest.fn().mockReturnValue("");
|
||||||
|
|
||||||
const consoleOutputSpy = jest.spyOn(process.stdout, "write").mockImplementation();
|
const consoleOutputSpy = jest.spyOn(process.stdout, "write").mockImplementation();
|
||||||
//Invoke and assert
|
//Invoke and assert
|
||||||
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
|
await expect(deployment.deploy(kubeCtl, ['manifests/deployment.yaml'], undefined)).resolves.not.toThrowError();
|
||||||
expect(consoleOutputSpy).toHaveBeenNthCalledWith(1, '##[warning]kubectl annotate failed' + os.EOL)
|
expect(consoleOutputSpy).toHaveBeenNthCalledWith(1, '::warning::kubectl annotate failed' + os.EOL)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("utility - getWorkflowFilePath() - Get workflow file path under API failure", async () => {
|
||||||
|
//Mocks
|
||||||
|
const errorWebResponse = {
|
||||||
|
'statusCode': httpClient.StatusCodes.UNAUTHORIZED,
|
||||||
|
'body': {}
|
||||||
|
} as httpClient.WebResponse
|
||||||
|
jest.spyOn(httpClient, 'sendRequest').mockImplementation(() => Promise.resolve(errorWebResponse));
|
||||||
|
|
||||||
|
//Invoke and assert
|
||||||
|
await expect(utility.getWorkflowFilePath(process.env.GITHUB_TOKEN)).resolves.not.toThrowError;
|
||||||
|
await expect(utility.getWorkflowFilePath(process.env.GITHUB_TOKEN)).resolves.toBe(process.env.GITHUB_WORKFLOW);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("action - run() - Throw kubectl error on 404 response", async () => {
|
||||||
|
const kubectlVersion = 'v1.18.0'
|
||||||
|
const arch = 'arm128';
|
||||||
|
// Mock
|
||||||
|
coreMock.getInput = jest.fn().mockReturnValue(kubectlVersion);
|
||||||
|
osMock.arch = jest.fn().mockReturnValue(arch);
|
||||||
|
toolCacheMock.find = jest.fn().mockReturnValue(undefined);
|
||||||
|
toolCacheMock.downloadTool = jest.fn().mockImplementation(_ => {
|
||||||
|
throw new toolCache.HTTPError(httpClient.StatusCodes.NOT_FOUND);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Invoke and assert
|
||||||
|
await expect(action.run()).rejects.toThrow(util.format("Kubectl '%s' for '%s' arch not found.", kubectlVersion, arch));
|
||||||
|
});
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ inputs:
|
|||||||
description: 'Deploy when a previous deployment already exists. If true then --force argument is added to the apply command'
|
description: 'Deploy when a previous deployment already exists. If true then --force argument is added to the apply command'
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
|
token:
|
||||||
|
description: 'Github token'
|
||||||
|
default: ${{ github.token }}
|
||||||
|
required: true
|
||||||
|
|
||||||
branding:
|
branding:
|
||||||
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
color: 'green' # optional, decorates the entry in the GitHub Marketplace
|
||||||
|
|||||||
+107
-107
@@ -1,107 +1,107 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.promote = void 0;
|
exports.promote = void 0;
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const deploymentHelper = require("../utilities/strategy-helpers/deployment-helper");
|
const deploymentHelper = require("../utilities/strategy-helpers/deployment-helper");
|
||||||
const canaryDeploymentHelper = require("../utilities/strategy-helpers/canary-deployment-helper");
|
const canaryDeploymentHelper = require("../utilities/strategy-helpers/canary-deployment-helper");
|
||||||
const SMICanaryDeploymentHelper = require("../utilities/strategy-helpers/smi-canary-deployment-helper");
|
const SMICanaryDeploymentHelper = require("../utilities/strategy-helpers/smi-canary-deployment-helper");
|
||||||
const utils = require("../utilities/manifest-utilities");
|
const utils = require("../utilities/manifest-utilities");
|
||||||
const TaskInputParameters = require("../input-parameters");
|
const TaskInputParameters = require("../input-parameters");
|
||||||
const manifest_utilities_1 = require("../utilities/manifest-utilities");
|
const manifest_utilities_1 = require("../utilities/manifest-utilities");
|
||||||
const KubernetesObjectUtility = require("../utilities/resource-object-utility");
|
const KubernetesObjectUtility = require("../utilities/resource-object-utility");
|
||||||
const models = require("../constants");
|
const models = require("../constants");
|
||||||
const KubernetesManifestUtility = require("../utilities/manifest-stability-utility");
|
const KubernetesManifestUtility = require("../utilities/manifest-stability-utility");
|
||||||
const blue_green_helper_1 = require("../utilities/strategy-helpers/blue-green-helper");
|
const blue_green_helper_1 = require("../utilities/strategy-helpers/blue-green-helper");
|
||||||
const blue_green_helper_2 = require("../utilities/strategy-helpers/blue-green-helper");
|
const blue_green_helper_2 = require("../utilities/strategy-helpers/blue-green-helper");
|
||||||
const service_blue_green_helper_1 = require("../utilities/strategy-helpers/service-blue-green-helper");
|
const service_blue_green_helper_1 = require("../utilities/strategy-helpers/service-blue-green-helper");
|
||||||
const ingress_blue_green_helper_1 = require("../utilities/strategy-helpers/ingress-blue-green-helper");
|
const ingress_blue_green_helper_1 = require("../utilities/strategy-helpers/ingress-blue-green-helper");
|
||||||
const smi_blue_green_helper_1 = require("../utilities/strategy-helpers/smi-blue-green-helper");
|
const smi_blue_green_helper_1 = require("../utilities/strategy-helpers/smi-blue-green-helper");
|
||||||
const kubectl_object_model_1 = require("../kubectl-object-model");
|
const kubectl_object_model_1 = require("../kubectl-object-model");
|
||||||
function promote() {
|
function promote() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const kubectl = new kubectl_object_model_1.Kubectl(yield utils.getKubectl(), TaskInputParameters.namespace, true);
|
const kubectl = new kubectl_object_model_1.Kubectl(yield utils.getKubectl(), TaskInputParameters.namespace, true);
|
||||||
if (canaryDeploymentHelper.isCanaryDeploymentStrategy()) {
|
if (canaryDeploymentHelper.isCanaryDeploymentStrategy()) {
|
||||||
yield promoteCanary(kubectl);
|
yield promoteCanary(kubectl);
|
||||||
}
|
}
|
||||||
else if (blue_green_helper_2.isBlueGreenDeploymentStrategy()) {
|
else if (blue_green_helper_2.isBlueGreenDeploymentStrategy()) {
|
||||||
yield promoteBlueGreen(kubectl);
|
yield promoteBlueGreen(kubectl);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.debug('Strategy is not canary or blue-green deployment. Invalid request.');
|
core.debug('Strategy is not canary or blue-green deployment. Invalid request.');
|
||||||
throw ('InvalidPromotetActionDeploymentStrategy');
|
throw ('InvalidPromotetActionDeploymentStrategy');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.promote = promote;
|
exports.promote = promote;
|
||||||
function promoteCanary(kubectl) {
|
function promoteCanary(kubectl) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let includeServices = false;
|
let includeServices = false;
|
||||||
if (canaryDeploymentHelper.isSMICanaryStrategy()) {
|
if (canaryDeploymentHelper.isSMICanaryStrategy()) {
|
||||||
includeServices = true;
|
includeServices = true;
|
||||||
// In case of SMI traffic split strategy when deployment is promoted, first we will redirect traffic to
|
// In case of SMI traffic split strategy when deployment is promoted, first we will redirect traffic to
|
||||||
// Canary deployment, then update stable deployment and then redirect traffic to stable deployment
|
// Canary deployment, then update stable deployment and then redirect traffic to stable deployment
|
||||||
core.debug('Redirecting traffic to canary deployment');
|
core.debug('Redirecting traffic to canary deployment');
|
||||||
SMICanaryDeploymentHelper.redirectTrafficToCanaryDeployment(kubectl, TaskInputParameters.manifests);
|
SMICanaryDeploymentHelper.redirectTrafficToCanaryDeployment(kubectl, TaskInputParameters.manifests);
|
||||||
core.debug('Deploying input manifests with SMI canary strategy');
|
core.debug('Deploying input manifests with SMI canary strategy');
|
||||||
yield deploymentHelper.deploy(kubectl, TaskInputParameters.manifests, 'None');
|
yield deploymentHelper.deploy(kubectl, TaskInputParameters.manifests, 'None');
|
||||||
core.debug('Redirecting traffic to stable deployment');
|
core.debug('Redirecting traffic to stable deployment');
|
||||||
SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(kubectl, TaskInputParameters.manifests);
|
SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(kubectl, TaskInputParameters.manifests);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.debug('Deploying input manifests');
|
core.debug('Deploying input manifests');
|
||||||
yield deploymentHelper.deploy(kubectl, TaskInputParameters.manifests, 'None');
|
yield deploymentHelper.deploy(kubectl, TaskInputParameters.manifests, 'None');
|
||||||
}
|
}
|
||||||
core.debug('Deployment strategy selected is Canary. Deleting canary and baseline workloads.');
|
core.debug('Deployment strategy selected is Canary. Deleting canary and baseline workloads.');
|
||||||
try {
|
try {
|
||||||
canaryDeploymentHelper.deleteCanaryDeployment(kubectl, TaskInputParameters.manifests, includeServices);
|
canaryDeploymentHelper.deleteCanaryDeployment(kubectl, TaskInputParameters.manifests, includeServices);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.warning('Exception occurred while deleting canary and baseline workloads. Exception: ' + ex);
|
core.warning('Exception occurred while deleting canary and baseline workloads. Exception: ' + ex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function promoteBlueGreen(kubectl) {
|
function promoteBlueGreen(kubectl) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
// updated container images and pull secrets
|
// updated container images and pull secrets
|
||||||
let inputManifestFiles = manifest_utilities_1.getUpdatedManifestFiles(TaskInputParameters.manifests);
|
let inputManifestFiles = manifest_utilities_1.getUpdatedManifestFiles(TaskInputParameters.manifests);
|
||||||
const manifestObjects = blue_green_helper_1.getManifestObjects(inputManifestFiles);
|
const manifestObjects = blue_green_helper_1.getManifestObjects(inputManifestFiles);
|
||||||
core.debug('deleting old deployment and making new ones');
|
core.debug('deleting old deployment and making new ones');
|
||||||
let result;
|
let result;
|
||||||
if (blue_green_helper_2.isIngressRoute()) {
|
if (blue_green_helper_2.isIngressRoute()) {
|
||||||
result = yield ingress_blue_green_helper_1.promoteBlueGreenIngress(kubectl, manifestObjects);
|
result = yield ingress_blue_green_helper_1.promoteBlueGreenIngress(kubectl, manifestObjects);
|
||||||
}
|
}
|
||||||
else if (blue_green_helper_2.isSMIRoute()) {
|
else if (blue_green_helper_2.isSMIRoute()) {
|
||||||
result = yield smi_blue_green_helper_1.promoteBlueGreenSMI(kubectl, manifestObjects);
|
result = yield smi_blue_green_helper_1.promoteBlueGreenSMI(kubectl, manifestObjects);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = yield service_blue_green_helper_1.promoteBlueGreenService(kubectl, manifestObjects);
|
result = yield service_blue_green_helper_1.promoteBlueGreenService(kubectl, manifestObjects);
|
||||||
}
|
}
|
||||||
// checking stability of newly created deployments
|
// checking stability of newly created deployments
|
||||||
const deployedManifestFiles = result.newFilePaths;
|
const deployedManifestFiles = result.newFilePaths;
|
||||||
const resources = KubernetesObjectUtility.getResources(deployedManifestFiles, models.deploymentTypes.concat([models.DiscoveryAndLoadBalancerResource.service]));
|
const resources = KubernetesObjectUtility.getResources(deployedManifestFiles, models.deploymentTypes.concat([models.DiscoveryAndLoadBalancerResource.service]));
|
||||||
yield KubernetesManifestUtility.checkManifestStability(kubectl, resources);
|
yield KubernetesManifestUtility.checkManifestStability(kubectl, resources);
|
||||||
core.debug('routing to new deployments');
|
core.debug('routing to new deployments');
|
||||||
if (blue_green_helper_2.isIngressRoute()) {
|
if (blue_green_helper_2.isIngressRoute()) {
|
||||||
ingress_blue_green_helper_1.routeBlueGreenIngress(kubectl, null, manifestObjects.serviceNameMap, manifestObjects.ingressEntityList);
|
ingress_blue_green_helper_1.routeBlueGreenIngress(kubectl, null, manifestObjects.serviceNameMap, manifestObjects.ingressEntityList);
|
||||||
blue_green_helper_1.deleteWorkloadsAndServicesWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
|
blue_green_helper_1.deleteWorkloadsAndServicesWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList, manifestObjects.serviceEntityList);
|
||||||
}
|
}
|
||||||
else if (blue_green_helper_2.isSMIRoute()) {
|
else if (blue_green_helper_2.isSMIRoute()) {
|
||||||
smi_blue_green_helper_1.routeBlueGreenSMI(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
|
smi_blue_green_helper_1.routeBlueGreenSMI(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
|
||||||
blue_green_helper_1.deleteWorkloadsWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
|
blue_green_helper_1.deleteWorkloadsWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
|
||||||
smi_blue_green_helper_1.cleanupSMI(kubectl, manifestObjects.serviceEntityList);
|
smi_blue_green_helper_1.cleanupSMI(kubectl, manifestObjects.serviceEntityList);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
service_blue_green_helper_1.routeBlueGreenService(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
|
service_blue_green_helper_1.routeBlueGreenService(kubectl, blue_green_helper_2.NONE_LABEL_VALUE, manifestObjects.serviceEntityList);
|
||||||
blue_green_helper_1.deleteWorkloadsWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
|
blue_green_helper_1.deleteWorkloadsWithLabel(kubectl, blue_green_helper_2.GREEN_LABEL_VALUE, manifestObjects.deploymentEntityList);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+65
-65
@@ -1,65 +1,65 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.reject = void 0;
|
exports.reject = void 0;
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const canaryDeploymentHelper = require("../utilities/strategy-helpers/canary-deployment-helper");
|
const canaryDeploymentHelper = require("../utilities/strategy-helpers/canary-deployment-helper");
|
||||||
const SMICanaryDeploymentHelper = require("../utilities/strategy-helpers/smi-canary-deployment-helper");
|
const SMICanaryDeploymentHelper = require("../utilities/strategy-helpers/smi-canary-deployment-helper");
|
||||||
const kubectl_object_model_1 = require("../kubectl-object-model");
|
const kubectl_object_model_1 = require("../kubectl-object-model");
|
||||||
const utils = require("../utilities/manifest-utilities");
|
const utils = require("../utilities/manifest-utilities");
|
||||||
const TaskInputParameters = require("../input-parameters");
|
const TaskInputParameters = require("../input-parameters");
|
||||||
const service_blue_green_helper_1 = require("../utilities/strategy-helpers/service-blue-green-helper");
|
const service_blue_green_helper_1 = require("../utilities/strategy-helpers/service-blue-green-helper");
|
||||||
const ingress_blue_green_helper_1 = require("../utilities/strategy-helpers/ingress-blue-green-helper");
|
const ingress_blue_green_helper_1 = require("../utilities/strategy-helpers/ingress-blue-green-helper");
|
||||||
const smi_blue_green_helper_1 = require("../utilities/strategy-helpers/smi-blue-green-helper");
|
const smi_blue_green_helper_1 = require("../utilities/strategy-helpers/smi-blue-green-helper");
|
||||||
const blue_green_helper_1 = require("../utilities/strategy-helpers/blue-green-helper");
|
const blue_green_helper_1 = require("../utilities/strategy-helpers/blue-green-helper");
|
||||||
const deployment_helper_1 = require("../utilities/strategy-helpers/deployment-helper");
|
const deployment_helper_1 = require("../utilities/strategy-helpers/deployment-helper");
|
||||||
function reject() {
|
function reject() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const kubectl = new kubectl_object_model_1.Kubectl(yield utils.getKubectl(), TaskInputParameters.namespace, true);
|
const kubectl = new kubectl_object_model_1.Kubectl(yield utils.getKubectl(), TaskInputParameters.namespace, true);
|
||||||
if (canaryDeploymentHelper.isCanaryDeploymentStrategy()) {
|
if (canaryDeploymentHelper.isCanaryDeploymentStrategy()) {
|
||||||
yield rejectCanary(kubectl);
|
yield rejectCanary(kubectl);
|
||||||
}
|
}
|
||||||
else if (blue_green_helper_1.isBlueGreenDeploymentStrategy()) {
|
else if (blue_green_helper_1.isBlueGreenDeploymentStrategy()) {
|
||||||
yield rejectBlueGreen(kubectl);
|
yield rejectBlueGreen(kubectl);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.debug('Strategy is not canary or blue-green deployment. Invalid request.');
|
core.debug('Strategy is not canary or blue-green deployment. Invalid request.');
|
||||||
throw ('InvalidDeletetActionDeploymentStrategy');
|
throw ('InvalidDeletetActionDeploymentStrategy');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.reject = reject;
|
exports.reject = reject;
|
||||||
function rejectCanary(kubectl) {
|
function rejectCanary(kubectl) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let includeServices = false;
|
let includeServices = false;
|
||||||
if (canaryDeploymentHelper.isSMICanaryStrategy()) {
|
if (canaryDeploymentHelper.isSMICanaryStrategy()) {
|
||||||
core.debug('Reject deployment with SMI canary strategy');
|
core.debug('Reject deployment with SMI canary strategy');
|
||||||
includeServices = true;
|
includeServices = true;
|
||||||
SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(kubectl, TaskInputParameters.manifests);
|
SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(kubectl, TaskInputParameters.manifests);
|
||||||
}
|
}
|
||||||
core.debug('Deployment strategy selected is Canary. Deleting baseline and canary workloads.');
|
core.debug('Deployment strategy selected is Canary. Deleting baseline and canary workloads.');
|
||||||
canaryDeploymentHelper.deleteCanaryDeployment(kubectl, TaskInputParameters.manifests, includeServices);
|
canaryDeploymentHelper.deleteCanaryDeployment(kubectl, TaskInputParameters.manifests, includeServices);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function rejectBlueGreen(kubectl) {
|
function rejectBlueGreen(kubectl) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let inputManifestFiles = deployment_helper_1.getManifestFiles(TaskInputParameters.manifests);
|
let inputManifestFiles = deployment_helper_1.getManifestFiles(TaskInputParameters.manifests);
|
||||||
if (blue_green_helper_1.isIngressRoute()) {
|
if (blue_green_helper_1.isIngressRoute()) {
|
||||||
yield ingress_blue_green_helper_1.rejectBlueGreenIngress(kubectl, inputManifestFiles);
|
yield ingress_blue_green_helper_1.rejectBlueGreenIngress(kubectl, inputManifestFiles);
|
||||||
}
|
}
|
||||||
else if (blue_green_helper_1.isSMIRoute()) {
|
else if (blue_green_helper_1.isSMIRoute()) {
|
||||||
yield smi_blue_green_helper_1.rejectBlueGreenSMI(kubectl, inputManifestFiles);
|
yield smi_blue_green_helper_1.rejectBlueGreenSMI(kubectl, inputManifestFiles);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
yield service_blue_green_helper_1.rejectBlueGreenService(kubectl, inputManifestFiles);
|
yield service_blue_green_helper_1.rejectBlueGreenService(kubectl, inputManifestFiles);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-39
@@ -1,39 +1,54 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.workflowAnnotations = exports.workloadTypesWithRolloutStatus = exports.workloadTypes = exports.deploymentTypes = exports.ServiceTypes = exports.DiscoveryAndLoadBalancerResource = exports.KubernetesWorkload = void 0;
|
exports.getWorkflowAnnotationKeyLabel = exports.getWorkflowAnnotationsJson = exports.workloadTypesWithRolloutStatus = exports.workloadTypes = exports.deploymentTypes = exports.ServiceTypes = exports.DiscoveryAndLoadBalancerResource = exports.KubernetesWorkload = void 0;
|
||||||
class KubernetesWorkload {
|
class KubernetesWorkload {
|
||||||
}
|
}
|
||||||
exports.KubernetesWorkload = KubernetesWorkload;
|
exports.KubernetesWorkload = KubernetesWorkload;
|
||||||
KubernetesWorkload.pod = 'Pod';
|
KubernetesWorkload.pod = 'Pod';
|
||||||
KubernetesWorkload.replicaset = 'Replicaset';
|
KubernetesWorkload.replicaset = 'Replicaset';
|
||||||
KubernetesWorkload.deployment = 'Deployment';
|
KubernetesWorkload.deployment = 'Deployment';
|
||||||
KubernetesWorkload.statefulSet = 'StatefulSet';
|
KubernetesWorkload.statefulSet = 'StatefulSet';
|
||||||
KubernetesWorkload.daemonSet = 'DaemonSet';
|
KubernetesWorkload.daemonSet = 'DaemonSet';
|
||||||
KubernetesWorkload.job = 'job';
|
KubernetesWorkload.job = 'job';
|
||||||
KubernetesWorkload.cronjob = 'cronjob';
|
KubernetesWorkload.cronjob = 'cronjob';
|
||||||
class DiscoveryAndLoadBalancerResource {
|
class DiscoveryAndLoadBalancerResource {
|
||||||
}
|
}
|
||||||
exports.DiscoveryAndLoadBalancerResource = DiscoveryAndLoadBalancerResource;
|
exports.DiscoveryAndLoadBalancerResource = DiscoveryAndLoadBalancerResource;
|
||||||
DiscoveryAndLoadBalancerResource.service = 'service';
|
DiscoveryAndLoadBalancerResource.service = 'service';
|
||||||
DiscoveryAndLoadBalancerResource.ingress = 'ingress';
|
DiscoveryAndLoadBalancerResource.ingress = 'ingress';
|
||||||
class ServiceTypes {
|
class ServiceTypes {
|
||||||
}
|
}
|
||||||
exports.ServiceTypes = ServiceTypes;
|
exports.ServiceTypes = ServiceTypes;
|
||||||
ServiceTypes.loadBalancer = 'LoadBalancer';
|
ServiceTypes.loadBalancer = 'LoadBalancer';
|
||||||
ServiceTypes.nodePort = 'NodePort';
|
ServiceTypes.nodePort = 'NodePort';
|
||||||
ServiceTypes.clusterIP = 'ClusterIP';
|
ServiceTypes.clusterIP = 'ClusterIP';
|
||||||
exports.deploymentTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset'];
|
exports.deploymentTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset'];
|
||||||
exports.workloadTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
|
exports.workloadTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
|
||||||
exports.workloadTypesWithRolloutStatus = ['deployment', 'daemonset', 'statefulset'];
|
exports.workloadTypesWithRolloutStatus = ['deployment', 'daemonset', 'statefulset'];
|
||||||
exports.workflowAnnotations = [
|
function getWorkflowAnnotationsJson(lastSuccessRunSha, workflowFilePath, deploymentConfig) {
|
||||||
`run=${process.env['GITHUB_RUN_ID']}`,
|
let annotationObject = {};
|
||||||
`repository=${process.env['GITHUB_REPOSITORY']}`,
|
annotationObject["run"] = process.env.GITHUB_RUN_ID;
|
||||||
`workflow=${process.env['GITHUB_WORKFLOW']}`,
|
annotationObject["repository"] = process.env.GITHUB_REPOSITORY;
|
||||||
`jobName=${process.env['GITHUB_JOB']}`,
|
annotationObject["workflow"] = process.env.GITHUB_WORKFLOW;
|
||||||
`createdBy=${process.env['GITHUB_ACTOR']}`,
|
annotationObject["workflowFileName"] = workflowFilePath.replace(".github/workflows/", "");
|
||||||
`runUri=https://github.com/${process.env['GITHUB_REPOSITORY']}/actions/runs/${process.env['GITHUB_RUN_ID']}`,
|
annotationObject["jobName"] = process.env.GITHUB_JOB;
|
||||||
`commit=${process.env['GITHUB_SHA']}`,
|
annotationObject["createdBy"] = process.env.GITHUB_ACTOR;
|
||||||
`branch=${process.env['GITHUB_REF']}`,
|
annotationObject["runUri"] = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
|
||||||
`deployTimestamp=${Date.now()}`,
|
annotationObject["commit"] = process.env.GITHUB_SHA;
|
||||||
`provider=GitHub`
|
annotationObject["lastSuccessRunCommit"] = lastSuccessRunSha;
|
||||||
];
|
annotationObject["branch"] = process.env.GITHUB_REF;
|
||||||
|
annotationObject["deployTimestamp"] = Date.now();
|
||||||
|
annotationObject["dockerfilePaths"] = deploymentConfig.dockerfilePaths;
|
||||||
|
annotationObject["manifestsPaths"] = deploymentConfig.manifestFilePaths;
|
||||||
|
annotationObject["helmChartPaths"] = deploymentConfig.helmChartFilePaths;
|
||||||
|
annotationObject["provider"] = "GitHub";
|
||||||
|
return JSON.stringify(annotationObject);
|
||||||
|
}
|
||||||
|
exports.getWorkflowAnnotationsJson = getWorkflowAnnotationsJson;
|
||||||
|
function getWorkflowAnnotationKeyLabel(workflowFilePath) {
|
||||||
|
const hashKey = require("crypto").createHash("MD5")
|
||||||
|
.update(`${process.env.GITHUB_REPOSITORY}/${workflowFilePath}`)
|
||||||
|
.digest("hex");
|
||||||
|
return `githubWorkflow_${hashKey}`;
|
||||||
|
}
|
||||||
|
exports.getWorkflowAnnotationKeyLabel = getWorkflowAnnotationKeyLabel;
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.DockerExec = void 0;
|
||||||
|
const tool_runner_1 = require("./utilities/tool-runner");
|
||||||
|
class DockerExec {
|
||||||
|
constructor(dockerPath) {
|
||||||
|
this.dockerPath = dockerPath;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
pull(image, args, silent) {
|
||||||
|
args = ['pull', image, ...args];
|
||||||
|
let result = this.execute(args, silent);
|
||||||
|
if (result.stderr != '' && result.code != 0) {
|
||||||
|
throw new Error(`docker images pull failed with: ${result.error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inspect(image, args, silent) {
|
||||||
|
args = ['inspect', image, ...args];
|
||||||
|
let result = this.execute(args, silent);
|
||||||
|
if (result.stderr != '' && result.code != 0) {
|
||||||
|
throw new Error(`docker inspect call failed with: ${result.error}`);
|
||||||
|
}
|
||||||
|
return result.stdout;
|
||||||
|
}
|
||||||
|
execute(args, silent) {
|
||||||
|
const command = new tool_runner_1.ToolRunner(this.dockerPath);
|
||||||
|
command.arg(args);
|
||||||
|
return command.execSync({ silent: !!silent });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.DockerExec = DockerExec;
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.GitHubClient = void 0;
|
||||||
|
const core = require("@actions/core");
|
||||||
|
const httpClient_1 = require("./utilities/httpClient");
|
||||||
|
class GitHubClient {
|
||||||
|
constructor(repository, token) {
|
||||||
|
this._repository = repository;
|
||||||
|
this._token = token;
|
||||||
|
}
|
||||||
|
getWorkflows() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const getWorkflowFileNameUrl = `https://api.github.com/repos/${this._repository}/actions/workflows`;
|
||||||
|
const webRequest = new httpClient_1.WebRequest();
|
||||||
|
webRequest.method = "GET";
|
||||||
|
webRequest.uri = getWorkflowFileNameUrl;
|
||||||
|
webRequest.headers = {
|
||||||
|
Authorization: `Bearer ${this._token}`
|
||||||
|
};
|
||||||
|
core.debug(`Getting workflows for repo: ${this._repository}`);
|
||||||
|
const response = yield httpClient_1.sendRequest(webRequest);
|
||||||
|
return Promise.resolve(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.GitHubClient = GitHubClient;
|
||||||
+57
-53
@@ -1,53 +1,57 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.forceDeployment = exports.args = exports.baselineAndCanaryReplicas = exports.versionSwitchBuffer = exports.routeMethod = exports.trafficSplitMethod = exports.deploymentStrategy = exports.canaryPercentage = exports.manifests = exports.imagePullSecrets = exports.containers = exports.namespace = void 0;
|
exports.githubToken = exports.forceDeployment = exports.args = exports.baselineAndCanaryReplicas = exports.versionSwitchBuffer = exports.routeMethod = exports.trafficSplitMethod = exports.deploymentStrategy = exports.canaryPercentage = exports.manifests = exports.imagePullSecrets = exports.containers = exports.namespace = void 0;
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
exports.namespace = core.getInput('namespace');
|
exports.namespace = core.getInput('namespace');
|
||||||
exports.containers = core.getInput('images').split('\n');
|
exports.containers = core.getInput('images').split('\n');
|
||||||
exports.imagePullSecrets = core.getInput('imagepullsecrets').split('\n').filter(secret => secret.trim().length > 0);
|
exports.imagePullSecrets = core.getInput('imagepullsecrets').split('\n').filter(secret => secret.trim().length > 0);
|
||||||
exports.manifests = core.getInput('manifests').split('\n');
|
exports.manifests = core.getInput('manifests').split(/[\n,;]+/).filter(manifest => manifest.trim().length > 0);
|
||||||
exports.canaryPercentage = core.getInput('percentage');
|
exports.canaryPercentage = core.getInput('percentage');
|
||||||
exports.deploymentStrategy = core.getInput('strategy');
|
exports.deploymentStrategy = core.getInput('strategy');
|
||||||
exports.trafficSplitMethod = core.getInput('traffic-split-method');
|
exports.trafficSplitMethod = core.getInput('traffic-split-method');
|
||||||
exports.routeMethod = core.getInput('route-method');
|
exports.routeMethod = core.getInput('route-method');
|
||||||
exports.versionSwitchBuffer = core.getInput('version-switch-buffer');
|
exports.versionSwitchBuffer = core.getInput('version-switch-buffer');
|
||||||
exports.baselineAndCanaryReplicas = core.getInput('baseline-and-canary-replicas');
|
exports.baselineAndCanaryReplicas = core.getInput('baseline-and-canary-replicas');
|
||||||
exports.args = core.getInput('arguments');
|
exports.args = core.getInput('arguments');
|
||||||
exports.forceDeployment = core.getInput('force').toLowerCase() == 'true';
|
exports.forceDeployment = core.getInput('force').toLowerCase() == 'true';
|
||||||
if (!exports.namespace) {
|
exports.githubToken = core.getInput("token");
|
||||||
core.debug('Namespace was not supplied; using "default" namespace instead.');
|
if (!exports.namespace) {
|
||||||
exports.namespace = 'default';
|
core.debug('Namespace was not supplied; using "default" namespace instead.');
|
||||||
}
|
exports.namespace = 'default';
|
||||||
try {
|
}
|
||||||
const pe = parseInt(exports.canaryPercentage);
|
if (!exports.githubToken) {
|
||||||
if (pe < 0 || pe > 100) {
|
core.error("'token' input is not supplied. Set it to a PAT/GITHUB_TOKEN");
|
||||||
core.setFailed('A valid percentage value is between 0 and 100');
|
}
|
||||||
process.exit(1);
|
try {
|
||||||
}
|
const pe = parseInt(exports.canaryPercentage);
|
||||||
}
|
if (pe < 0 || pe > 100) {
|
||||||
catch (ex) {
|
core.setFailed('A valid percentage value is between 0 and 100');
|
||||||
core.setFailed("Enter a valid 'percentage' integer value ");
|
process.exit(1);
|
||||||
process.exit(1);
|
}
|
||||||
}
|
}
|
||||||
try {
|
catch (ex) {
|
||||||
const pe = parseInt(exports.baselineAndCanaryReplicas);
|
core.setFailed("Enter a valid 'percentage' integer value ");
|
||||||
if (pe < 0 || pe > 100) {
|
process.exit(1);
|
||||||
core.setFailed('A valid baseline-and-canary-replicas value is between 0 and 100');
|
}
|
||||||
process.exit(1);
|
try {
|
||||||
}
|
const pe = parseInt(exports.baselineAndCanaryReplicas);
|
||||||
}
|
if (pe < 0 || pe > 100) {
|
||||||
catch (ex) {
|
core.setFailed('A valid baseline-and-canary-replicas value is between 0 and 100');
|
||||||
core.setFailed("Enter a valid 'baseline-and-canary-replicas' integer value");
|
process.exit(1);
|
||||||
process.exit(1);
|
}
|
||||||
}
|
}
|
||||||
try {
|
catch (ex) {
|
||||||
const pe = parseInt(exports.versionSwitchBuffer);
|
core.setFailed("Enter a valid 'baseline-and-canary-replicas' integer value");
|
||||||
if (pe < 0 || pe > 300) {
|
process.exit(1);
|
||||||
core.setFailed('Invalid buffer time, valid version-switch-buffer is a value more than or equal to 0 and lesser than or equal 300');
|
}
|
||||||
process.exit(1);
|
try {
|
||||||
}
|
const pe = parseInt(exports.versionSwitchBuffer);
|
||||||
}
|
if (pe < 0 || pe > 300) {
|
||||||
catch (ex) {
|
core.setFailed('Invalid buffer time, valid version-switch-buffer is a value more than or equal to 0 and lesser than or equal 300');
|
||||||
core.setFailed("Enter a valid 'version-switch-buffer' integer value");
|
process.exit(1);
|
||||||
process.exit(1);
|
}
|
||||||
}
|
}
|
||||||
|
catch (ex) {
|
||||||
|
core.setFailed("Enter a valid 'version-switch-buffer' integer value");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|||||||
+117
-114
@@ -1,114 +1,117 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.Kubectl = void 0;
|
exports.Kubectl = void 0;
|
||||||
const tool_runner_1 = require("./utilities/tool-runner");
|
const tool_runner_1 = require("./utilities/tool-runner");
|
||||||
class Kubectl {
|
class Kubectl {
|
||||||
constructor(kubectlPath, namespace, ignoreSSLErrors) {
|
constructor(kubectlPath, namespace, ignoreSSLErrors) {
|
||||||
this.kubectlPath = kubectlPath;
|
this.kubectlPath = kubectlPath;
|
||||||
this.ignoreSSLErrors = !!ignoreSSLErrors;
|
this.ignoreSSLErrors = !!ignoreSSLErrors;
|
||||||
if (!!namespace) {
|
if (!!namespace) {
|
||||||
this.namespace = namespace;
|
this.namespace = namespace;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.namespace = 'default';
|
this.namespace = 'default';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apply(configurationPaths, force) {
|
apply(configurationPaths, force) {
|
||||||
let applyArgs = ['apply', '-f', this.createInlineArray(configurationPaths)];
|
let applyArgs = ['apply', '-f', this.createInlineArray(configurationPaths)];
|
||||||
if (!!force) {
|
if (!!force) {
|
||||||
console.log("force flag is on, deployment will continue even if previous deployment already exists");
|
console.log("force flag is on, deployment will continue even if previous deployment already exists");
|
||||||
applyArgs.push('--force');
|
applyArgs.push('--force');
|
||||||
}
|
}
|
||||||
return this.execute(applyArgs);
|
return this.execute(applyArgs);
|
||||||
}
|
}
|
||||||
describe(resourceType, resourceName, silent) {
|
describe(resourceType, resourceName, silent) {
|
||||||
return this.execute(['describe', resourceType, resourceName], silent);
|
return this.execute(['describe', resourceType, resourceName], silent);
|
||||||
}
|
}
|
||||||
getNewReplicaSet(deployment) {
|
getNewReplicaSet(deployment) {
|
||||||
let newReplicaSet = '';
|
let newReplicaSet = '';
|
||||||
const result = this.describe('deployment', deployment, true);
|
const result = this.describe('deployment', deployment, true);
|
||||||
if (result && result.stdout) {
|
if (result && result.stdout) {
|
||||||
const stdout = result.stdout.split('\n');
|
const stdout = result.stdout.split('\n');
|
||||||
stdout.forEach((line) => {
|
stdout.forEach((line) => {
|
||||||
if (!!line && line.toLowerCase().indexOf('newreplicaset') > -1) {
|
if (!!line && line.toLowerCase().indexOf('newreplicaset') > -1) {
|
||||||
newReplicaSet = line.substr(14).trim().split(' ')[0];
|
newReplicaSet = line.substr(14).trim().split(' ')[0];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return newReplicaSet;
|
return newReplicaSet;
|
||||||
}
|
}
|
||||||
annotate(resourceType, resourceName, annotations, overwrite) {
|
annotate(resourceType, resourceName, annotation) {
|
||||||
let args = ['annotate', resourceType, resourceName];
|
let args = ['annotate', resourceType, resourceName];
|
||||||
args = args.concat(annotations);
|
args.push(annotation);
|
||||||
if (!!overwrite) {
|
args.push(`--overwrite`);
|
||||||
args.push(`--overwrite`);
|
return this.execute(args);
|
||||||
}
|
}
|
||||||
return this.execute(args);
|
annotateFiles(files, annotation) {
|
||||||
}
|
let args = ['annotate'];
|
||||||
annotateFiles(files, annotations, overwrite) {
|
args = args.concat(['-f', this.createInlineArray(files)]);
|
||||||
let args = ['annotate'];
|
args.push(annotation);
|
||||||
args = args.concat(['-f', this.createInlineArray(files)]);
|
args.push(`--overwrite`);
|
||||||
args = args.concat(annotations);
|
return this.execute(args);
|
||||||
if (!!overwrite) {
|
}
|
||||||
args.push(`--overwrite`);
|
labelFiles(files, labels) {
|
||||||
}
|
let args = ['label'];
|
||||||
return this.execute(args);
|
args = args.concat(['-f', this.createInlineArray(files)]);
|
||||||
}
|
args = args.concat(labels);
|
||||||
getAllPods() {
|
args.push(`--overwrite`);
|
||||||
return this.execute(['get', 'pods', '-o', 'json'], true);
|
return this.execute(args);
|
||||||
}
|
}
|
||||||
getClusterInfo() {
|
getAllPods() {
|
||||||
return this.execute(['cluster-info'], true);
|
return this.execute(['get', 'pods', '-o', 'json'], true);
|
||||||
}
|
}
|
||||||
checkRolloutStatus(resourceType, name) {
|
getClusterInfo() {
|
||||||
return this.execute(['rollout', 'status', resourceType + '/' + name]);
|
return this.execute(['cluster-info'], true);
|
||||||
}
|
}
|
||||||
getResource(resourceType, name) {
|
checkRolloutStatus(resourceType, name) {
|
||||||
return this.execute(['get', resourceType + '/' + name, '-o', 'json']);
|
return this.execute(['rollout', 'status', resourceType + '/' + name]);
|
||||||
}
|
}
|
||||||
getResources(applyOutput, filterResourceTypes) {
|
getResource(resourceType, name) {
|
||||||
const outputLines = applyOutput.split('\n');
|
return this.execute(['get', resourceType + '/' + name, '-o', 'json']);
|
||||||
const results = [];
|
}
|
||||||
outputLines.forEach(line => {
|
getResources(applyOutput, filterResourceTypes) {
|
||||||
const words = line.split(' ');
|
const outputLines = applyOutput.split('\n');
|
||||||
if (words.length > 2) {
|
const results = [];
|
||||||
const resourceType = words[0].trim();
|
outputLines.forEach(line => {
|
||||||
const resourceName = JSON.parse(words[1].trim());
|
const words = line.split(' ');
|
||||||
if (filterResourceTypes.filter(type => !!type && resourceType.toLowerCase().startsWith(type.toLowerCase())).length > 0) {
|
if (words.length > 2) {
|
||||||
results.push({
|
const resourceType = words[0].trim();
|
||||||
type: resourceType,
|
const resourceName = JSON.parse(words[1].trim());
|
||||||
name: resourceName
|
if (filterResourceTypes.filter(type => !!type && resourceType.toLowerCase().startsWith(type.toLowerCase())).length > 0) {
|
||||||
});
|
results.push({
|
||||||
}
|
type: resourceType,
|
||||||
}
|
name: resourceName
|
||||||
});
|
});
|
||||||
return results;
|
}
|
||||||
}
|
}
|
||||||
executeCommand(customCommand, args) {
|
});
|
||||||
if (!customCommand)
|
return results;
|
||||||
throw new Error('NullCommandForKubectl');
|
}
|
||||||
return args ? this.execute([customCommand, args]) : this.execute([customCommand]);
|
executeCommand(customCommand, args) {
|
||||||
}
|
if (!customCommand)
|
||||||
delete(args) {
|
throw new Error('NullCommandForKubectl');
|
||||||
if (typeof args === 'string')
|
return args ? this.execute([customCommand, args]) : this.execute([customCommand]);
|
||||||
return this.execute(['delete', args]);
|
}
|
||||||
else
|
delete(args) {
|
||||||
return this.execute(['delete'].concat(args));
|
if (typeof args === 'string')
|
||||||
}
|
return this.execute(['delete', args]);
|
||||||
execute(args, silent) {
|
else
|
||||||
if (this.ignoreSSLErrors) {
|
return this.execute(['delete'].concat(args));
|
||||||
args.push('--insecure-skip-tls-verify');
|
}
|
||||||
}
|
execute(args, silent) {
|
||||||
args = args.concat(['--namespace', this.namespace]);
|
if (this.ignoreSSLErrors) {
|
||||||
const command = new tool_runner_1.ToolRunner(this.kubectlPath);
|
args.push('--insecure-skip-tls-verify');
|
||||||
command.arg(args);
|
}
|
||||||
return command.execSync({ silent: !!silent });
|
args = args.concat(['--namespace', this.namespace]);
|
||||||
}
|
const command = new tool_runner_1.ToolRunner(this.kubectlPath);
|
||||||
createInlineArray(str) {
|
command.arg(args);
|
||||||
if (typeof str === 'string') {
|
return command.execSync({ silent: !!silent });
|
||||||
return str;
|
}
|
||||||
}
|
createInlineArray(str) {
|
||||||
return str.join(',');
|
if (typeof str === 'string') {
|
||||||
}
|
return str;
|
||||||
}
|
}
|
||||||
exports.Kubectl = Kubectl;
|
return str.join(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.Kubectl = Kubectl;
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
||||||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
const os = require("os");
|
|
||||||
const path = require("path");
|
|
||||||
const util = require("util");
|
|
||||||
const fs = require("fs");
|
|
||||||
const toolCache = require("@actions/tool-cache");
|
|
||||||
const core = require("@actions/core");
|
|
||||||
const kubectlToolName = 'kubectl';
|
|
||||||
const stableKubectlVersion = 'v1.15.0';
|
|
||||||
const stableVersionUrl = 'https://storage.googleapis.com/kubernetes-release/release/stable.txt';
|
|
||||||
function getExecutableExtension() {
|
|
||||||
if (os.type().match(/^Win/)) {
|
|
||||||
return '.exe';
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
function getkubectlDownloadURL(version) {
|
|
||||||
switch (os.type()) {
|
|
||||||
case 'Linux':
|
|
||||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/amd64/kubectl', version);
|
|
||||||
case 'Darwin':
|
|
||||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/amd64/kubectl', version);
|
|
||||||
case 'Windows_NT':
|
|
||||||
default:
|
|
||||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/amd64/kubectl.exe', version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function getStableKubectlVersion() {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
return toolCache.downloadTool(stableVersionUrl).then((downloadPath) => {
|
|
||||||
let version = fs.readFileSync(downloadPath, 'utf8').toString().trim();
|
|
||||||
if (!version) {
|
|
||||||
version = stableKubectlVersion;
|
|
||||||
}
|
|
||||||
return version;
|
|
||||||
}, (error) => {
|
|
||||||
core.debug(error);
|
|
||||||
core.warning('GetStableVersionFailed');
|
|
||||||
return stableKubectlVersion;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.getStableKubectlVersion = getStableKubectlVersion;
|
|
||||||
function downloadKubectl(version) {
|
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
|
||||||
let cachedToolpath = toolCache.find(kubectlToolName, version);
|
|
||||||
let kubectlDownloadPath = '';
|
|
||||||
if (!cachedToolpath) {
|
|
||||||
try {
|
|
||||||
kubectlDownloadPath = yield toolCache.downloadTool(getkubectlDownloadURL(version));
|
|
||||||
}
|
|
||||||
catch (exception) {
|
|
||||||
throw new Error('DownloadKubectlFailed');
|
|
||||||
}
|
|
||||||
cachedToolpath = yield toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + getExecutableExtension(), kubectlToolName, version);
|
|
||||||
}
|
|
||||||
const kubectlPath = path.join(cachedToolpath, kubectlToolName + getExecutableExtension());
|
|
||||||
fs.chmodSync(kubectlPath, '777');
|
|
||||||
return kubectlPath;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.downloadKubectl = downloadKubectl;
|
|
||||||
function getTrafficSplitAPIVersion(kubectl) {
|
|
||||||
const result = kubectl.executeCommand('api-versions');
|
|
||||||
const trafficSplitAPIVersion = result.stdout.split('\n').find(version => version.startsWith('split.smi-spec.io'));
|
|
||||||
if (trafficSplitAPIVersion == null || typeof trafficSplitAPIVersion == 'undefined') {
|
|
||||||
throw new Error('UnableToCreateTrafficSplitManifestFile');
|
|
||||||
}
|
|
||||||
return trafficSplitAPIVersion;
|
|
||||||
}
|
|
||||||
exports.getTrafficSplitAPIVersion = getTrafficSplitAPIVersion;
|
|
||||||
+96
-91
@@ -1,91 +1,96 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.run = void 0;
|
exports.run = void 0;
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const io = require("@actions/io");
|
const io = require("@actions/io");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const toolCache = require("@actions/tool-cache");
|
const toolCache = require("@actions/tool-cache");
|
||||||
const kubectl_util_1 = require("./utilities/kubectl-util");
|
const kubectl_util_1 = require("./utilities/kubectl-util");
|
||||||
const utility_1 = require("./utilities/utility");
|
const utility_1 = require("./utilities/utility");
|
||||||
const kubectl_object_model_1 = require("./kubectl-object-model");
|
const kubectl_object_model_1 = require("./kubectl-object-model");
|
||||||
const deployment_helper_1 = require("./utilities/strategy-helpers/deployment-helper");
|
const deployment_helper_1 = require("./utilities/strategy-helpers/deployment-helper");
|
||||||
const promote_1 = require("./actions/promote");
|
const promote_1 = require("./actions/promote");
|
||||||
const reject_1 = require("./actions/reject");
|
const reject_1 = require("./actions/reject");
|
||||||
let kubectlPath = "";
|
let kubectlPath = "";
|
||||||
function setKubectlPath() {
|
function setKubectlPath() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
if (core.getInput('kubectl-version')) {
|
if (core.getInput('kubectl-version')) {
|
||||||
const version = core.getInput('kubectl-version');
|
const version = core.getInput('kubectl-version');
|
||||||
kubectlPath = toolCache.find('kubectl', version);
|
kubectlPath = toolCache.find('kubectl', version);
|
||||||
if (!kubectlPath) {
|
if (!kubectlPath) {
|
||||||
kubectlPath = yield installKubectl(version);
|
kubectlPath = yield installKubectl(version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
kubectlPath = yield io.which('kubectl', false);
|
kubectlPath = yield io.which('kubectl', false);
|
||||||
if (!kubectlPath) {
|
if (!kubectlPath) {
|
||||||
const allVersions = toolCache.findAllVersions('kubectl');
|
const allVersions = toolCache.findAllVersions('kubectl');
|
||||||
kubectlPath = allVersions.length > 0 ? toolCache.find('kubectl', allVersions[0]) : '';
|
kubectlPath = allVersions.length > 0 ? toolCache.find('kubectl', allVersions[0]) : '';
|
||||||
if (!kubectlPath) {
|
if (!kubectlPath) {
|
||||||
throw new Error('Kubectl is not installed, either add install-kubectl action or provide "kubectl-version" input to download kubectl');
|
throw new Error('Kubectl is not installed, either add install-kubectl action or provide "kubectl-version" input to download kubectl');
|
||||||
}
|
}
|
||||||
kubectlPath = path.join(kubectlPath, `kubectl${utility_1.getExecutableExtension()}`);
|
kubectlPath = path.join(kubectlPath, `kubectl${utility_1.getExecutableExtension()}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function installKubectl(version) {
|
function installKubectl(version) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
if (utility_1.isEqual(version, 'latest')) {
|
if (utility_1.isEqual(version, 'latest')) {
|
||||||
version = yield kubectl_util_1.getStableKubectlVersion();
|
version = yield kubectl_util_1.getStableKubectlVersion();
|
||||||
}
|
}
|
||||||
return yield kubectl_util_1.downloadKubectl(version);
|
return yield kubectl_util_1.downloadKubectl(version);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function checkClusterContext() {
|
function checkClusterContext() {
|
||||||
if (!process.env["KUBECONFIG"]) {
|
if (!process.env["KUBECONFIG"]) {
|
||||||
throw new Error('Cluster context not set. Use k8ssetcontext action to set cluster context');
|
core.warning('KUBECONFIG env is not explicitly set. Ensure cluster context is set by using k8s-set-context / aks-set-context action.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function run() {
|
function run() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
checkClusterContext();
|
checkClusterContext();
|
||||||
yield setKubectlPath();
|
yield setKubectlPath();
|
||||||
let manifestsInput = core.getInput('manifests');
|
let manifestsInput = core.getInput('manifests');
|
||||||
if (!manifestsInput) {
|
if (!manifestsInput) {
|
||||||
core.setFailed('No manifests supplied to deploy');
|
core.setFailed('No manifests supplied to deploy');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let namespace = core.getInput('namespace');
|
let namespace = core.getInput('namespace');
|
||||||
if (!namespace) {
|
if (!namespace) {
|
||||||
namespace = 'default';
|
namespace = 'default';
|
||||||
}
|
}
|
||||||
let action = core.getInput('action');
|
let action = core.getInput('action');
|
||||||
let manifests = manifestsInput.split('\n');
|
let manifests = manifestsInput.split(/[\n,;]+/).filter(manifest => manifest.trim().length > 0);
|
||||||
if (action === 'deploy') {
|
if (manifests.length > 0) {
|
||||||
let strategy = core.getInput('strategy');
|
manifests = manifests.map(manifest => {
|
||||||
console.log("strategy: ", strategy);
|
return manifest.trim();
|
||||||
yield deployment_helper_1.deploy(new kubectl_object_model_1.Kubectl(kubectlPath, namespace), manifests, strategy);
|
});
|
||||||
}
|
}
|
||||||
else if (action === 'promote') {
|
if (action === 'deploy') {
|
||||||
yield promote_1.promote();
|
let strategy = core.getInput('strategy');
|
||||||
}
|
console.log("strategy: ", strategy);
|
||||||
else if (action === 'reject') {
|
yield deployment_helper_1.deploy(new kubectl_object_model_1.Kubectl(kubectlPath, namespace), manifests, strategy);
|
||||||
yield reject_1.reject();
|
}
|
||||||
}
|
else if (action === 'promote') {
|
||||||
else {
|
yield promote_1.promote();
|
||||||
core.setFailed('Not a valid action. The allowed actions are deploy, promote, reject');
|
}
|
||||||
}
|
else if (action === 'reject') {
|
||||||
});
|
yield reject_1.reject();
|
||||||
}
|
}
|
||||||
exports.run = run;
|
else {
|
||||||
run().catch(core.setFailed);
|
core.setFailed('Not a valid action. The allowed actions are deploy, promote, reject');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.run = run;
|
||||||
|
run().catch(core.setFailed);
|
||||||
|
|||||||
@@ -1,78 +1,78 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.writeManifestToFile = exports.writeObjectsToFile = exports.assertFileExists = exports.ensureDirExists = exports.getNewUserDirPath = exports.getTempDirectory = void 0;
|
exports.writeManifestToFile = exports.writeObjectsToFile = exports.assertFileExists = exports.ensureDirExists = exports.getNewUserDirPath = exports.getTempDirectory = void 0;
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const os = require("os");
|
const os = require("os");
|
||||||
function getTempDirectory() {
|
function getTempDirectory() {
|
||||||
return process.env['runner.tempDirectory'] || os.tmpdir();
|
return process.env['runner.tempDirectory'] || os.tmpdir();
|
||||||
}
|
}
|
||||||
exports.getTempDirectory = getTempDirectory;
|
exports.getTempDirectory = getTempDirectory;
|
||||||
function getNewUserDirPath() {
|
function getNewUserDirPath() {
|
||||||
let userDir = path.join(getTempDirectory(), 'kubectlTask');
|
let userDir = path.join(getTempDirectory(), 'kubectlTask');
|
||||||
ensureDirExists(userDir);
|
ensureDirExists(userDir);
|
||||||
userDir = path.join(userDir, getCurrentTime().toString());
|
userDir = path.join(userDir, getCurrentTime().toString());
|
||||||
ensureDirExists(userDir);
|
ensureDirExists(userDir);
|
||||||
return userDir;
|
return userDir;
|
||||||
}
|
}
|
||||||
exports.getNewUserDirPath = getNewUserDirPath;
|
exports.getNewUserDirPath = getNewUserDirPath;
|
||||||
function ensureDirExists(dirPath) {
|
function ensureDirExists(dirPath) {
|
||||||
if (!fs.existsSync(dirPath)) {
|
if (!fs.existsSync(dirPath)) {
|
||||||
fs.mkdirSync(dirPath);
|
fs.mkdirSync(dirPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.ensureDirExists = ensureDirExists;
|
exports.ensureDirExists = ensureDirExists;
|
||||||
function assertFileExists(path) {
|
function assertFileExists(path) {
|
||||||
if (!fs.existsSync(path)) {
|
if (!fs.existsSync(path)) {
|
||||||
core.error(`FileNotFoundException : ${path}`);
|
core.error(`FileNotFoundException : ${path}`);
|
||||||
throw new Error(`FileNotFoundException: ${path}`);
|
throw new Error(`FileNotFoundException: ${path}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.assertFileExists = assertFileExists;
|
exports.assertFileExists = assertFileExists;
|
||||||
function writeObjectsToFile(inputObjects) {
|
function writeObjectsToFile(inputObjects) {
|
||||||
const newFilePaths = [];
|
const newFilePaths = [];
|
||||||
if (!!inputObjects) {
|
if (!!inputObjects) {
|
||||||
inputObjects.forEach((inputObject) => {
|
inputObjects.forEach((inputObject) => {
|
||||||
try {
|
try {
|
||||||
const inputObjectString = JSON.stringify(inputObject);
|
const inputObjectString = JSON.stringify(inputObject);
|
||||||
if (!!inputObject.kind && !!inputObject.metadata && !!inputObject.metadata.name) {
|
if (!!inputObject.kind && !!inputObject.metadata && !!inputObject.metadata.name) {
|
||||||
const fileName = getManifestFileName(inputObject.kind, inputObject.metadata.name);
|
const fileName = getManifestFileName(inputObject.kind, inputObject.metadata.name);
|
||||||
fs.writeFileSync(path.join(fileName), inputObjectString);
|
fs.writeFileSync(path.join(fileName), inputObjectString);
|
||||||
newFilePaths.push(fileName);
|
newFilePaths.push(fileName);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.debug('Input object is not proper K8s resource object. Object: ' + inputObjectString);
|
core.debug('Input object is not proper K8s resource object. Object: ' + inputObjectString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.debug('Exception occurred while writing object to file : ' + inputObject + ' . Exception: ' + ex);
|
core.debug('Exception occurred while writing object to file : ' + inputObject + ' . Exception: ' + ex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return newFilePaths;
|
return newFilePaths;
|
||||||
}
|
}
|
||||||
exports.writeObjectsToFile = writeObjectsToFile;
|
exports.writeObjectsToFile = writeObjectsToFile;
|
||||||
function writeManifestToFile(inputObjectString, kind, name) {
|
function writeManifestToFile(inputObjectString, kind, name) {
|
||||||
if (inputObjectString) {
|
if (inputObjectString) {
|
||||||
try {
|
try {
|
||||||
const fileName = getManifestFileName(kind, name);
|
const fileName = getManifestFileName(kind, name);
|
||||||
fs.writeFileSync(path.join(fileName), inputObjectString);
|
fs.writeFileSync(path.join(fileName), inputObjectString);
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.debug('Exception occurred while writing object to file : ' + inputObjectString + ' . Exception: ' + ex);
|
core.debug('Exception occurred while writing object to file : ' + inputObjectString + ' . Exception: ' + ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
exports.writeManifestToFile = writeManifestToFile;
|
exports.writeManifestToFile = writeManifestToFile;
|
||||||
function getManifestFileName(kind, name) {
|
function getManifestFileName(kind, name) {
|
||||||
const filePath = kind + '_' + name + '_' + getCurrentTime().toString();
|
const filePath = kind + '_' + name + '_' + getCurrentTime().toString();
|
||||||
const tempDirectory = getTempDirectory();
|
const tempDirectory = getTempDirectory();
|
||||||
const fileName = path.join(tempDirectory, path.basename(filePath));
|
const fileName = path.join(tempDirectory, path.basename(filePath));
|
||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
function getCurrentTime() {
|
function getCurrentTime() {
|
||||||
return new Date().getTime();
|
return new Date().getTime();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.sleepFor = exports.sendRequest = exports.WebRequestOptions = exports.WebResponse = exports.WebRequest = exports.StatusCodes = void 0;
|
||||||
|
// Taken from https://github.com/Azure/aks-set-context/blob/master/src/client.ts
|
||||||
|
const util = require("util");
|
||||||
|
const fs = require("fs");
|
||||||
|
const httpClient = require("typed-rest-client/HttpClient");
|
||||||
|
const core = require("@actions/core");
|
||||||
|
var httpCallbackClient = new httpClient.HttpClient('GITHUB_RUNNER', null, {});
|
||||||
|
var StatusCodes;
|
||||||
|
(function (StatusCodes) {
|
||||||
|
StatusCodes[StatusCodes["OK"] = 200] = "OK";
|
||||||
|
StatusCodes[StatusCodes["CREATED"] = 201] = "CREATED";
|
||||||
|
StatusCodes[StatusCodes["ACCEPTED"] = 202] = "ACCEPTED";
|
||||||
|
StatusCodes[StatusCodes["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
|
||||||
|
StatusCodes[StatusCodes["NOT_FOUND"] = 404] = "NOT_FOUND";
|
||||||
|
StatusCodes[StatusCodes["INTERNAL_SERVER_ERROR"] = 500] = "INTERNAL_SERVER_ERROR";
|
||||||
|
StatusCodes[StatusCodes["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
|
||||||
|
})(StatusCodes = exports.StatusCodes || (exports.StatusCodes = {}));
|
||||||
|
class WebRequest {
|
||||||
|
}
|
||||||
|
exports.WebRequest = WebRequest;
|
||||||
|
class WebResponse {
|
||||||
|
}
|
||||||
|
exports.WebResponse = WebResponse;
|
||||||
|
class WebRequestOptions {
|
||||||
|
}
|
||||||
|
exports.WebRequestOptions = WebRequestOptions;
|
||||||
|
function sendRequest(request, options) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let i = 0;
|
||||||
|
let retryCount = options && options.retryCount ? options.retryCount : 5;
|
||||||
|
let retryIntervalInSeconds = options && options.retryIntervalInSeconds ? options.retryIntervalInSeconds : 2;
|
||||||
|
let retriableErrorCodes = options && options.retriableErrorCodes ? options.retriableErrorCodes : ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN"];
|
||||||
|
let retriableStatusCodes = options && options.retriableStatusCodes ? options.retriableStatusCodes : [408, 409, 500, 502, 503, 504];
|
||||||
|
let timeToWait = retryIntervalInSeconds;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
if (request.body && typeof (request.body) !== 'string' && !request.body["readable"]) {
|
||||||
|
request.body = fs.createReadStream(request.body["path"]);
|
||||||
|
}
|
||||||
|
let response = yield sendRequestInternal(request);
|
||||||
|
if (retriableStatusCodes.indexOf(response.statusCode) != -1 && ++i < retryCount) {
|
||||||
|
core.debug(util.format("Encountered a retriable status code: %s. Message: '%s'.", response.statusCode, response.statusMessage));
|
||||||
|
yield sleepFor(timeToWait);
|
||||||
|
timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (retriableErrorCodes.indexOf(error.code) != -1 && ++i < retryCount) {
|
||||||
|
core.debug(util.format("Encountered a retriable error:%s. Message: %s.", error.code, error.message));
|
||||||
|
yield sleepFor(timeToWait);
|
||||||
|
timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (error.code) {
|
||||||
|
core.debug("error code =" + error.code);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.sendRequest = sendRequest;
|
||||||
|
function sleepFor(sleepDurationInSeconds) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
setTimeout(resolve, sleepDurationInSeconds * 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.sleepFor = sleepFor;
|
||||||
|
function sendRequestInternal(request) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
core.debug(util.format("[%s]%s", request.method, request.uri));
|
||||||
|
var response = yield httpCallbackClient.request(request.method, request.uri, request.body, request.headers);
|
||||||
|
return yield toWebResponse(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function toWebResponse(response) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
var res = new WebResponse();
|
||||||
|
if (response) {
|
||||||
|
res.statusCode = response.message.statusCode;
|
||||||
|
res.statusMessage = response.message.statusMessage;
|
||||||
|
res.headers = response.message.headers;
|
||||||
|
var body = yield response.readBody();
|
||||||
|
if (body) {
|
||||||
|
try {
|
||||||
|
res.body = JSON.parse(body);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
core.debug("Could not parse response: " + JSON.stringify(error));
|
||||||
|
core.debug("Response: " + JSON.stringify(res.body));
|
||||||
|
res.body = body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ const os = require("os");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const toolCache = require("@actions/tool-cache");
|
const toolCache = require("@actions/tool-cache");
|
||||||
const util = require("util");
|
const util = require("util");
|
||||||
|
const httpClient_1 = require("./httpClient");
|
||||||
const kubectlToolName = 'kubectl';
|
const kubectlToolName = 'kubectl';
|
||||||
const stableKubectlVersion = 'v1.15.0';
|
const stableKubectlVersion = 'v1.15.0';
|
||||||
const stableVersionUrl = 'https://storage.googleapis.com/kubernetes-release/release/stable.txt';
|
const stableVersionUrl = 'https://storage.googleapis.com/kubernetes-release/release/stable.txt';
|
||||||
@@ -26,15 +27,22 @@ function getExecutableExtension() {
|
|||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
function getkubectlDownloadURL(version) {
|
function getKubectlArch() {
|
||||||
|
let arch = os.arch();
|
||||||
|
if (arch === 'x64') {
|
||||||
|
return 'amd64';
|
||||||
|
}
|
||||||
|
return arch;
|
||||||
|
}
|
||||||
|
function getkubectlDownloadURL(version, arch) {
|
||||||
switch (os.type()) {
|
switch (os.type()) {
|
||||||
case 'Linux':
|
case 'Linux':
|
||||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/amd64/kubectl', version);
|
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/%s/kubectl', version, arch);
|
||||||
case 'Darwin':
|
case 'Darwin':
|
||||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/amd64/kubectl', version);
|
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/%s/kubectl', version, arch);
|
||||||
case 'Windows_NT':
|
case 'Windows_NT':
|
||||||
default:
|
default:
|
||||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/amd64/kubectl.exe', version);
|
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/%s/kubectl.exe', version, arch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.getkubectlDownloadURL = getkubectlDownloadURL;
|
exports.getkubectlDownloadURL = getkubectlDownloadURL;
|
||||||
@@ -58,12 +66,18 @@ function downloadKubectl(version) {
|
|||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let cachedToolpath = toolCache.find(kubectlToolName, version);
|
let cachedToolpath = toolCache.find(kubectlToolName, version);
|
||||||
let kubectlDownloadPath = '';
|
let kubectlDownloadPath = '';
|
||||||
|
let arch = getKubectlArch();
|
||||||
if (!cachedToolpath) {
|
if (!cachedToolpath) {
|
||||||
try {
|
try {
|
||||||
kubectlDownloadPath = yield toolCache.downloadTool(getkubectlDownloadURL(version));
|
kubectlDownloadPath = yield toolCache.downloadTool(getkubectlDownloadURL(version, arch));
|
||||||
}
|
}
|
||||||
catch (exception) {
|
catch (exception) {
|
||||||
throw new Error('DownloadKubectlFailed');
|
if (exception instanceof toolCache.HTTPError && exception.httpStatusCode === httpClient_1.StatusCodes.NOT_FOUND) {
|
||||||
|
throw new Error(util.format("Kubectl '%s' for '%s' arch not found.", version, arch));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new Error('DownloadKubectlFailed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cachedToolpath = yield toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + getExecutableExtension(), kubectlToolName, version);
|
cachedToolpath = yield toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + getExecutableExtension(), kubectlToolName, version);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,158 +1,158 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.checkPodStatus = exports.checkManifestStability = void 0;
|
exports.checkPodStatus = exports.checkManifestStability = void 0;
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const utils = require("./utility");
|
const utils = require("./utility");
|
||||||
const KubernetesConstants = require("../constants");
|
const KubernetesConstants = require("../constants");
|
||||||
function checkManifestStability(kubectl, resources) {
|
function checkManifestStability(kubectl, resources) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let rolloutStatusHasErrors = false;
|
let rolloutStatusHasErrors = false;
|
||||||
const numberOfResources = resources.length;
|
const numberOfResources = resources.length;
|
||||||
for (let i = 0; i < numberOfResources; i++) {
|
for (let i = 0; i < numberOfResources; i++) {
|
||||||
const resource = resources[i];
|
const resource = resources[i];
|
||||||
if (KubernetesConstants.workloadTypesWithRolloutStatus.indexOf(resource.type.toLowerCase()) >= 0) {
|
if (KubernetesConstants.workloadTypesWithRolloutStatus.indexOf(resource.type.toLowerCase()) >= 0) {
|
||||||
try {
|
try {
|
||||||
var result = kubectl.checkRolloutStatus(resource.type, resource.name);
|
var result = kubectl.checkRolloutStatus(resource.type, resource.name);
|
||||||
utils.checkForErrors([result]);
|
utils.checkForErrors([result]);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.error(ex);
|
core.error(ex);
|
||||||
kubectl.describe(resource.type, resource.name);
|
kubectl.describe(resource.type, resource.name);
|
||||||
rolloutStatusHasErrors = true;
|
rolloutStatusHasErrors = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (utils.isEqual(resource.type, KubernetesConstants.KubernetesWorkload.pod, true)) {
|
if (utils.isEqual(resource.type, KubernetesConstants.KubernetesWorkload.pod, true)) {
|
||||||
try {
|
try {
|
||||||
yield checkPodStatus(kubectl, resource.name);
|
yield checkPodStatus(kubectl, resource.name);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.warning(`CouldNotDeterminePodStatus ${JSON.stringify(ex)}`);
|
core.warning(`CouldNotDeterminePodStatus ${JSON.stringify(ex)}`);
|
||||||
kubectl.describe(resource.type, resource.name);
|
kubectl.describe(resource.type, resource.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (utils.isEqual(resource.type, KubernetesConstants.DiscoveryAndLoadBalancerResource.service, true)) {
|
if (utils.isEqual(resource.type, KubernetesConstants.DiscoveryAndLoadBalancerResource.service, true)) {
|
||||||
try {
|
try {
|
||||||
const service = getService(kubectl, resource.name);
|
const service = getService(kubectl, resource.name);
|
||||||
const spec = service.spec;
|
const spec = service.spec;
|
||||||
const status = service.status;
|
const status = service.status;
|
||||||
if (utils.isEqual(spec.type, KubernetesConstants.ServiceTypes.loadBalancer, true)) {
|
if (utils.isEqual(spec.type, KubernetesConstants.ServiceTypes.loadBalancer, true)) {
|
||||||
if (!isLoadBalancerIPAssigned(status)) {
|
if (!isLoadBalancerIPAssigned(status)) {
|
||||||
yield waitForServiceExternalIPAssignment(kubectl, resource.name);
|
yield waitForServiceExternalIPAssignment(kubectl, resource.name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log('ServiceExternalIP', resource.name, status.loadBalancer.ingress[0].ip);
|
console.log('ServiceExternalIP', resource.name, status.loadBalancer.ingress[0].ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.warning(`CouldNotDetermineServiceStatus of: ${resource.name} Error: ${JSON.stringify(ex)}`);
|
core.warning(`CouldNotDetermineServiceStatus of: ${resource.name} Error: ${JSON.stringify(ex)}`);
|
||||||
kubectl.describe(resource.type, resource.name);
|
kubectl.describe(resource.type, resource.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rolloutStatusHasErrors) {
|
if (rolloutStatusHasErrors) {
|
||||||
throw new Error('RolloutStatusTimedout');
|
throw new Error('RolloutStatusTimedout');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.checkManifestStability = checkManifestStability;
|
exports.checkManifestStability = checkManifestStability;
|
||||||
function checkPodStatus(kubectl, podName) {
|
function checkPodStatus(kubectl, podName) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const sleepTimeout = 10 * 1000; // 10 seconds
|
const sleepTimeout = 10 * 1000; // 10 seconds
|
||||||
const iterations = 60; // 60 * 10 seconds timeout = 10 minutes max timeout
|
const iterations = 60; // 60 * 10 seconds timeout = 10 minutes max timeout
|
||||||
let podStatus;
|
let podStatus;
|
||||||
let kubectlDescribeNeeded = false;
|
let kubectlDescribeNeeded = false;
|
||||||
for (let i = 0; i < iterations; i++) {
|
for (let i = 0; i < iterations; i++) {
|
||||||
yield utils.sleep(sleepTimeout);
|
yield utils.sleep(sleepTimeout);
|
||||||
core.debug(`Polling for pod status: ${podName}`);
|
core.debug(`Polling for pod status: ${podName}`);
|
||||||
podStatus = getPodStatus(kubectl, podName);
|
podStatus = getPodStatus(kubectl, podName);
|
||||||
if (podStatus.phase && podStatus.phase !== 'Pending' && podStatus.phase !== 'Unknown') {
|
if (podStatus.phase && podStatus.phase !== 'Pending' && podStatus.phase !== 'Unknown') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
podStatus = getPodStatus(kubectl, podName);
|
podStatus = getPodStatus(kubectl, podName);
|
||||||
switch (podStatus.phase) {
|
switch (podStatus.phase) {
|
||||||
case 'Succeeded':
|
case 'Succeeded':
|
||||||
case 'Running':
|
case 'Running':
|
||||||
if (isPodReady(podStatus)) {
|
if (isPodReady(podStatus)) {
|
||||||
console.log(`pod/${podName} is successfully rolled out`);
|
console.log(`pod/${podName} is successfully rolled out`);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
kubectlDescribeNeeded = true;
|
kubectlDescribeNeeded = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'Pending':
|
case 'Pending':
|
||||||
if (!isPodReady(podStatus)) {
|
if (!isPodReady(podStatus)) {
|
||||||
core.warning(`pod/${podName} rollout status check timedout`);
|
core.warning(`pod/${podName} rollout status check timedout`);
|
||||||
kubectlDescribeNeeded = true;
|
kubectlDescribeNeeded = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'Failed':
|
case 'Failed':
|
||||||
core.error(`pod/${podName} rollout failed`);
|
core.error(`pod/${podName} rollout failed`);
|
||||||
kubectlDescribeNeeded = true;
|
kubectlDescribeNeeded = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
core.warning(`pod/${podName} rollout status: ${podStatus.phase}`);
|
core.warning(`pod/${podName} rollout status: ${podStatus.phase}`);
|
||||||
}
|
}
|
||||||
if (kubectlDescribeNeeded) {
|
if (kubectlDescribeNeeded) {
|
||||||
kubectl.describe('pod', podName);
|
kubectl.describe('pod', podName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.checkPodStatus = checkPodStatus;
|
exports.checkPodStatus = checkPodStatus;
|
||||||
function getPodStatus(kubectl, podName) {
|
function getPodStatus(kubectl, podName) {
|
||||||
const podResult = kubectl.getResource('pod', podName);
|
const podResult = kubectl.getResource('pod', podName);
|
||||||
utils.checkForErrors([podResult]);
|
utils.checkForErrors([podResult]);
|
||||||
const podStatus = JSON.parse(podResult.stdout).status;
|
const podStatus = JSON.parse(podResult.stdout).status;
|
||||||
core.debug(`Pod Status: ${JSON.stringify(podStatus)}`);
|
core.debug(`Pod Status: ${JSON.stringify(podStatus)}`);
|
||||||
return podStatus;
|
return podStatus;
|
||||||
}
|
}
|
||||||
function isPodReady(podStatus) {
|
function isPodReady(podStatus) {
|
||||||
let allContainersAreReady = true;
|
let allContainersAreReady = true;
|
||||||
podStatus.containerStatuses.forEach(container => {
|
podStatus.containerStatuses.forEach(container => {
|
||||||
if (container.ready === false) {
|
if (container.ready === false) {
|
||||||
console.log(`'${container.name}' status: ${JSON.stringify(container.state)}`);
|
console.log(`'${container.name}' status: ${JSON.stringify(container.state)}`);
|
||||||
allContainersAreReady = false;
|
allContainersAreReady = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!allContainersAreReady) {
|
if (!allContainersAreReady) {
|
||||||
core.warning('AllContainersNotInReadyState');
|
core.warning('AllContainersNotInReadyState');
|
||||||
}
|
}
|
||||||
return allContainersAreReady;
|
return allContainersAreReady;
|
||||||
}
|
}
|
||||||
function getService(kubectl, serviceName) {
|
function getService(kubectl, serviceName) {
|
||||||
const serviceResult = kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.service, serviceName);
|
const serviceResult = kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.service, serviceName);
|
||||||
utils.checkForErrors([serviceResult]);
|
utils.checkForErrors([serviceResult]);
|
||||||
return JSON.parse(serviceResult.stdout);
|
return JSON.parse(serviceResult.stdout);
|
||||||
}
|
}
|
||||||
function waitForServiceExternalIPAssignment(kubectl, serviceName) {
|
function waitForServiceExternalIPAssignment(kubectl, serviceName) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
const sleepTimeout = 10 * 1000; // 10 seconds
|
const sleepTimeout = 10 * 1000; // 10 seconds
|
||||||
const iterations = 18; // 18 * 10 seconds timeout = 3 minutes max timeout
|
const iterations = 18; // 18 * 10 seconds timeout = 3 minutes max timeout
|
||||||
for (let i = 0; i < iterations; i++) {
|
for (let i = 0; i < iterations; i++) {
|
||||||
console.log(`waitForServiceIpAssignment : ${serviceName}`);
|
console.log(`waitForServiceIpAssignment : ${serviceName}`);
|
||||||
yield utils.sleep(sleepTimeout);
|
yield utils.sleep(sleepTimeout);
|
||||||
let status = (getService(kubectl, serviceName)).status;
|
let status = (getService(kubectl, serviceName)).status;
|
||||||
if (isLoadBalancerIPAssigned(status)) {
|
if (isLoadBalancerIPAssigned(status)) {
|
||||||
console.log('ServiceExternalIP', serviceName, status.loadBalancer.ingress[0].ip);
|
console.log('ServiceExternalIP', serviceName, status.loadBalancer.ingress[0].ip);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
core.warning(`waitForServiceIpAssignmentTimedOut ${serviceName}`);
|
core.warning(`waitForServiceIpAssignmentTimedOut ${serviceName}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function isLoadBalancerIPAssigned(status) {
|
function isLoadBalancerIPAssigned(status) {
|
||||||
if (status && status.loadBalancer && status.loadBalancer.ingress && status.loadBalancer.ingress.length > 0) {
|
if (status && status.loadBalancer && status.loadBalancer.ingress && status.loadBalancer.ingress.length > 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+283
-283
@@ -1,283 +1,283 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.isWorkloadEntity = exports.getUpdatedManifestFiles = exports.updateImagePullSecrets = exports.substituteImageNameInSpecFile = exports.getDeleteCmdArgs = exports.createKubectlArgs = exports.getKubectl = exports.getManifestFiles = void 0;
|
exports.isWorkloadEntity = exports.getUpdatedManifestFiles = exports.updateImagePullSecrets = exports.substituteImageNameInSpecFile = exports.getDeleteCmdArgs = exports.createKubectlArgs = exports.getKubectl = exports.getManifestFiles = void 0;
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const yaml = require("js-yaml");
|
const yaml = require("js-yaml");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const kubectlutility = require("./kubectl-util");
|
const kubectlutility = require("./kubectl-util");
|
||||||
const io = require("@actions/io");
|
const io = require("@actions/io");
|
||||||
const utility_1 = require("./utility");
|
const utility_1 = require("./utility");
|
||||||
const fileHelper = require("./files-helper");
|
const fileHelper = require("./files-helper");
|
||||||
const KubernetesObjectUtility = require("./resource-object-utility");
|
const KubernetesObjectUtility = require("./resource-object-utility");
|
||||||
const TaskInputParameters = require("../input-parameters");
|
const TaskInputParameters = require("../input-parameters");
|
||||||
function getManifestFiles(manifestFilePaths) {
|
function getManifestFiles(manifestFilePaths) {
|
||||||
if (!manifestFilePaths) {
|
if (!manifestFilePaths) {
|
||||||
core.debug('file input is not present');
|
core.debug('file input is not present');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return manifestFilePaths;
|
return manifestFilePaths;
|
||||||
}
|
}
|
||||||
exports.getManifestFiles = getManifestFiles;
|
exports.getManifestFiles = getManifestFiles;
|
||||||
function getKubectl() {
|
function getKubectl() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
try {
|
try {
|
||||||
return Promise.resolve(io.which('kubectl', true));
|
return Promise.resolve(io.which('kubectl', true));
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
return kubectlutility.downloadKubectl(yield kubectlutility.getStableKubectlVersion());
|
return kubectlutility.downloadKubectl(yield kubectlutility.getStableKubectlVersion());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.getKubectl = getKubectl;
|
exports.getKubectl = getKubectl;
|
||||||
function createKubectlArgs(kinds, names) {
|
function createKubectlArgs(kinds, names) {
|
||||||
let args = '';
|
let args = '';
|
||||||
if (!!kinds && kinds.size > 0) {
|
if (!!kinds && kinds.size > 0) {
|
||||||
args = args + createInlineArray(Array.from(kinds.values()));
|
args = args + createInlineArray(Array.from(kinds.values()));
|
||||||
}
|
}
|
||||||
if (!!names && names.size > 0) {
|
if (!!names && names.size > 0) {
|
||||||
args = args + ' ' + Array.from(names.values()).join(' ');
|
args = args + ' ' + Array.from(names.values()).join(' ');
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
exports.createKubectlArgs = createKubectlArgs;
|
exports.createKubectlArgs = createKubectlArgs;
|
||||||
function getDeleteCmdArgs(argsPrefix, inputArgs) {
|
function getDeleteCmdArgs(argsPrefix, inputArgs) {
|
||||||
let args = '';
|
let args = '';
|
||||||
if (!!argsPrefix && argsPrefix.length > 0) {
|
if (!!argsPrefix && argsPrefix.length > 0) {
|
||||||
args = argsPrefix;
|
args = argsPrefix;
|
||||||
}
|
}
|
||||||
if (!!inputArgs && inputArgs.length > 0) {
|
if (!!inputArgs && inputArgs.length > 0) {
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
args = args + ' ';
|
args = args + ' ';
|
||||||
}
|
}
|
||||||
args = args + inputArgs;
|
args = args + inputArgs;
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
exports.getDeleteCmdArgs = getDeleteCmdArgs;
|
exports.getDeleteCmdArgs = getDeleteCmdArgs;
|
||||||
/*
|
/*
|
||||||
For example,
|
For example,
|
||||||
currentString: `image: "example/example-image"`
|
currentString: `image: "example/example-image"`
|
||||||
imageName: `example/example-image`
|
imageName: `example/example-image`
|
||||||
imageNameWithNewTag: `example/example-image:identifiertag`
|
imageNameWithNewTag: `example/example-image:identifiertag`
|
||||||
|
|
||||||
This substituteImageNameInSpecFile function would return
|
This substituteImageNameInSpecFile function would return
|
||||||
return Value: `image: "example/example-image:identifiertag"`
|
return Value: `image: "example/example-image:identifiertag"`
|
||||||
*/
|
*/
|
||||||
function substituteImageNameInSpecFile(currentString, imageName, imageNameWithNewTag) {
|
function substituteImageNameInSpecFile(currentString, imageName, imageNameWithNewTag) {
|
||||||
if (currentString.indexOf(imageName) < 0) {
|
if (currentString.indexOf(imageName) < 0) {
|
||||||
core.debug(`No occurence of replacement token: ${imageName} found`);
|
core.debug(`No occurence of replacement token: ${imageName} found`);
|
||||||
return currentString;
|
return currentString;
|
||||||
}
|
}
|
||||||
return currentString.split('\n').reduce((acc, line) => {
|
return currentString.split('\n').reduce((acc, line) => {
|
||||||
const imageKeyword = line.match(/^ *image:/);
|
const imageKeyword = line.match(/^ *image:/);
|
||||||
if (imageKeyword) {
|
if (imageKeyword) {
|
||||||
let [currentImageName, currentImageTag] = line
|
let [currentImageName, currentImageTag] = line
|
||||||
.substring(imageKeyword[0].length) // consume the line from keyword onwards
|
.substring(imageKeyword[0].length) // consume the line from keyword onwards
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/[',"]/g, '') // replace allowed quotes with nothing
|
.replace(/[',"]/g, '') // replace allowed quotes with nothing
|
||||||
.split(':');
|
.split(':');
|
||||||
if (!currentImageTag && currentImageName.indexOf(' ') > 0) {
|
if (!currentImageTag && currentImageName.indexOf(' ') > 0) {
|
||||||
currentImageName = currentImageName.split(' ')[0]; // Stripping off comments
|
currentImageName = currentImageName.split(' ')[0]; // Stripping off comments
|
||||||
}
|
}
|
||||||
if (currentImageName === imageName) {
|
if (currentImageName === imageName) {
|
||||||
return acc + `${imageKeyword[0]} ${imageNameWithNewTag}\n`;
|
return acc + `${imageKeyword[0]} ${imageNameWithNewTag}\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return acc + line + '\n';
|
return acc + line + '\n';
|
||||||
}, '');
|
}, '');
|
||||||
}
|
}
|
||||||
exports.substituteImageNameInSpecFile = substituteImageNameInSpecFile;
|
exports.substituteImageNameInSpecFile = substituteImageNameInSpecFile;
|
||||||
function createInlineArray(str) {
|
function createInlineArray(str) {
|
||||||
if (typeof str === 'string') {
|
if (typeof str === 'string') {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
return str.join(',');
|
return str.join(',');
|
||||||
}
|
}
|
||||||
function getImagePullSecrets(inputObject) {
|
function getImagePullSecrets(inputObject) {
|
||||||
if (!inputObject || !inputObject.spec) {
|
if (!inputObject || !inputObject.spec) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (utility_1.isEqual(inputObject.kind, 'pod')
|
if (utility_1.isEqual(inputObject.kind, 'pod')
|
||||||
&& inputObject
|
&& inputObject
|
||||||
&& inputObject.spec
|
&& inputObject.spec
|
||||||
&& inputObject.spec.imagePullSecrets) {
|
&& inputObject.spec.imagePullSecrets) {
|
||||||
return inputObject.spec.imagePullSecrets;
|
return inputObject.spec.imagePullSecrets;
|
||||||
}
|
}
|
||||||
else if (utility_1.isEqual(inputObject.kind, 'cronjob')
|
else if (utility_1.isEqual(inputObject.kind, 'cronjob')
|
||||||
&& inputObject
|
&& inputObject
|
||||||
&& inputObject.spec
|
&& inputObject.spec
|
||||||
&& inputObject.spec.jobTemplate
|
&& inputObject.spec.jobTemplate
|
||||||
&& inputObject.spec.jobTemplate.spec
|
&& inputObject.spec.jobTemplate.spec
|
||||||
&& inputObject.spec.jobTemplate.spec.template
|
&& inputObject.spec.jobTemplate.spec.template
|
||||||
&& inputObject.spec.jobTemplate.spec.template.spec
|
&& inputObject.spec.jobTemplate.spec.template.spec
|
||||||
&& inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets) {
|
&& inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets) {
|
||||||
return inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets;
|
return inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets;
|
||||||
}
|
}
|
||||||
else if (inputObject
|
else if (inputObject
|
||||||
&& inputObject.spec
|
&& inputObject.spec
|
||||||
&& inputObject.spec.template
|
&& inputObject.spec.template
|
||||||
&& inputObject.spec.template.spec
|
&& inputObject.spec.template.spec
|
||||||
&& inputObject.spec.template.spec.imagePullSecrets) {
|
&& inputObject.spec.template.spec.imagePullSecrets) {
|
||||||
return inputObject.spec.template.spec.imagePullSecrets;
|
return inputObject.spec.template.spec.imagePullSecrets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function setImagePullSecrets(inputObject, newImagePullSecrets) {
|
function setImagePullSecrets(inputObject, newImagePullSecrets) {
|
||||||
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (utility_1.isEqual(inputObject.kind, 'pod')) {
|
if (utility_1.isEqual(inputObject.kind, 'pod')) {
|
||||||
if (inputObject
|
if (inputObject
|
||||||
&& inputObject.spec) {
|
&& inputObject.spec) {
|
||||||
if (newImagePullSecrets.length > 0) {
|
if (newImagePullSecrets.length > 0) {
|
||||||
inputObject.spec.imagePullSecrets = newImagePullSecrets;
|
inputObject.spec.imagePullSecrets = newImagePullSecrets;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
delete inputObject.spec.imagePullSecrets;
|
delete inputObject.spec.imagePullSecrets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (utility_1.isEqual(inputObject.kind, 'cronjob')) {
|
else if (utility_1.isEqual(inputObject.kind, 'cronjob')) {
|
||||||
if (inputObject
|
if (inputObject
|
||||||
&& inputObject.spec
|
&& inputObject.spec
|
||||||
&& inputObject.spec.jobTemplate
|
&& inputObject.spec.jobTemplate
|
||||||
&& inputObject.spec.jobTemplate.spec
|
&& inputObject.spec.jobTemplate.spec
|
||||||
&& inputObject.spec.jobTemplate.spec.template
|
&& inputObject.spec.jobTemplate.spec.template
|
||||||
&& inputObject.spec.jobTemplate.spec.template.spec) {
|
&& inputObject.spec.jobTemplate.spec.template.spec) {
|
||||||
if (newImagePullSecrets.length > 0) {
|
if (newImagePullSecrets.length > 0) {
|
||||||
inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
delete inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets;
|
delete inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!!inputObject.spec.template && !!inputObject.spec.template.spec) {
|
else if (!!inputObject.spec.template && !!inputObject.spec.template.spec) {
|
||||||
if (inputObject
|
if (inputObject
|
||||||
&& inputObject.spec
|
&& inputObject.spec
|
||||||
&& inputObject.spec.template
|
&& inputObject.spec.template
|
||||||
&& inputObject.spec.template.spec) {
|
&& inputObject.spec.template.spec) {
|
||||||
if (newImagePullSecrets.length > 0) {
|
if (newImagePullSecrets.length > 0) {
|
||||||
inputObject.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
inputObject.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
delete inputObject.spec.template.spec.imagePullSecrets;
|
delete inputObject.spec.template.spec.imagePullSecrets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function substituteImageNameInSpecContent(currentString, imageName, imageNameWithNewTag) {
|
function substituteImageNameInSpecContent(currentString, imageName, imageNameWithNewTag) {
|
||||||
if (currentString.indexOf(imageName) < 0) {
|
if (currentString.indexOf(imageName) < 0) {
|
||||||
core.debug(`No occurence of replacement token: ${imageName} found`);
|
core.debug(`No occurence of replacement token: ${imageName} found`);
|
||||||
return currentString;
|
return currentString;
|
||||||
}
|
}
|
||||||
return currentString.split('\n').reduce((acc, line) => {
|
return currentString.split('\n').reduce((acc, line) => {
|
||||||
const imageKeyword = line.match(/^ *image:/);
|
const imageKeyword = line.match(/^ *image:/);
|
||||||
if (imageKeyword) {
|
if (imageKeyword) {
|
||||||
const [currentImageName, currentImageTag] = line
|
const [currentImageName, currentImageTag] = line
|
||||||
.substring(imageKeyword[0].length) // consume the line from keyword onwards
|
.substring(imageKeyword[0].length) // consume the line from keyword onwards
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/[',"]/g, '') // replace allowed quotes with nothing
|
.replace(/[',"]/g, '') // replace allowed quotes with nothing
|
||||||
.split(':');
|
.split(':');
|
||||||
if (currentImageName === imageName) {
|
if (currentImageName === imageName) {
|
||||||
return acc + `${imageKeyword[0]} ${imageNameWithNewTag}\n`;
|
return acc + `${imageKeyword[0]} ${imageNameWithNewTag}\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return acc + line + '\n';
|
return acc + line + '\n';
|
||||||
}, '');
|
}, '');
|
||||||
}
|
}
|
||||||
function updateContainerImagesInManifestFiles(filePaths, containers) {
|
function updateContainerImagesInManifestFiles(filePaths, containers) {
|
||||||
if (!!containers && containers.length > 0) {
|
if (!!containers && containers.length > 0) {
|
||||||
const newFilePaths = [];
|
const newFilePaths = [];
|
||||||
const tempDirectory = fileHelper.getTempDirectory();
|
const tempDirectory = fileHelper.getTempDirectory();
|
||||||
filePaths.forEach((filePath) => {
|
filePaths.forEach((filePath) => {
|
||||||
let contents = fs.readFileSync(filePath).toString();
|
let contents = fs.readFileSync(filePath).toString();
|
||||||
containers.forEach((container) => {
|
containers.forEach((container) => {
|
||||||
let imageName = container.split(':')[0];
|
let imageName = container.split(':')[0];
|
||||||
if (imageName.indexOf('@') > 0) {
|
if (imageName.indexOf('@') > 0) {
|
||||||
imageName = imageName.split('@')[0];
|
imageName = imageName.split('@')[0];
|
||||||
}
|
}
|
||||||
if (contents.indexOf(imageName) > 0) {
|
if (contents.indexOf(imageName) > 0) {
|
||||||
contents = substituteImageNameInSpecFile(contents, imageName, container);
|
contents = substituteImageNameInSpecFile(contents, imageName, container);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const fileName = path.join(tempDirectory, path.basename(filePath));
|
const fileName = path.join(tempDirectory, path.basename(filePath));
|
||||||
fs.writeFileSync(path.join(fileName), contents);
|
fs.writeFileSync(path.join(fileName), contents);
|
||||||
newFilePaths.push(fileName);
|
newFilePaths.push(fileName);
|
||||||
});
|
});
|
||||||
return newFilePaths;
|
return newFilePaths;
|
||||||
}
|
}
|
||||||
return filePaths;
|
return filePaths;
|
||||||
}
|
}
|
||||||
function updateImagePullSecrets(inputObject, newImagePullSecrets) {
|
function updateImagePullSecrets(inputObject, newImagePullSecrets) {
|
||||||
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let newImagePullSecretsObjects;
|
let newImagePullSecretsObjects;
|
||||||
if (newImagePullSecrets.length > 0) {
|
if (newImagePullSecrets.length > 0) {
|
||||||
newImagePullSecretsObjects = Array.from(newImagePullSecrets, x => { return !!x ? { 'name': x } : null; });
|
newImagePullSecretsObjects = Array.from(newImagePullSecrets, x => { return !!x ? { 'name': x } : null; });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
newImagePullSecretsObjects = [];
|
newImagePullSecretsObjects = [];
|
||||||
}
|
}
|
||||||
let existingImagePullSecretObjects = getImagePullSecrets(inputObject);
|
let existingImagePullSecretObjects = getImagePullSecrets(inputObject);
|
||||||
if (!existingImagePullSecretObjects) {
|
if (!existingImagePullSecretObjects) {
|
||||||
existingImagePullSecretObjects = new Array();
|
existingImagePullSecretObjects = new Array();
|
||||||
}
|
}
|
||||||
existingImagePullSecretObjects = existingImagePullSecretObjects.concat(newImagePullSecretsObjects);
|
existingImagePullSecretObjects = existingImagePullSecretObjects.concat(newImagePullSecretsObjects);
|
||||||
setImagePullSecrets(inputObject, existingImagePullSecretObjects);
|
setImagePullSecrets(inputObject, existingImagePullSecretObjects);
|
||||||
}
|
}
|
||||||
exports.updateImagePullSecrets = updateImagePullSecrets;
|
exports.updateImagePullSecrets = updateImagePullSecrets;
|
||||||
function updateImagePullSecretsInManifestFiles(filePaths, imagePullSecrets) {
|
function updateImagePullSecretsInManifestFiles(filePaths, imagePullSecrets) {
|
||||||
if (!!imagePullSecrets && imagePullSecrets.length > 0) {
|
if (!!imagePullSecrets && imagePullSecrets.length > 0) {
|
||||||
const newObjectsList = [];
|
const newObjectsList = [];
|
||||||
filePaths.forEach((filePath) => {
|
filePaths.forEach((filePath) => {
|
||||||
const fileContents = fs.readFileSync(filePath).toString();
|
const fileContents = fs.readFileSync(filePath).toString();
|
||||||
yaml.safeLoadAll(fileContents, function (inputObject) {
|
yaml.safeLoadAll(fileContents, function (inputObject) {
|
||||||
if (!!inputObject && !!inputObject.kind) {
|
if (!!inputObject && !!inputObject.kind) {
|
||||||
const kind = inputObject.kind;
|
const kind = inputObject.kind;
|
||||||
if (KubernetesObjectUtility.isWorkloadEntity(kind)) {
|
if (KubernetesObjectUtility.isWorkloadEntity(kind)) {
|
||||||
KubernetesObjectUtility.updateImagePullSecrets(inputObject, imagePullSecrets, false);
|
KubernetesObjectUtility.updateImagePullSecrets(inputObject, imagePullSecrets, false);
|
||||||
}
|
}
|
||||||
newObjectsList.push(inputObject);
|
newObjectsList.push(inputObject);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
core.debug('New K8s objects after adding imagePullSecrets are :' + JSON.stringify(newObjectsList));
|
core.debug('New K8s objects after adding imagePullSecrets are :' + JSON.stringify(newObjectsList));
|
||||||
const newFilePaths = fileHelper.writeObjectsToFile(newObjectsList);
|
const newFilePaths = fileHelper.writeObjectsToFile(newObjectsList);
|
||||||
return newFilePaths;
|
return newFilePaths;
|
||||||
}
|
}
|
||||||
return filePaths;
|
return filePaths;
|
||||||
}
|
}
|
||||||
function getUpdatedManifestFiles(manifestFilePaths) {
|
function getUpdatedManifestFiles(manifestFilePaths) {
|
||||||
let inputManifestFiles = getManifestFiles(manifestFilePaths);
|
let inputManifestFiles = getManifestFiles(manifestFilePaths);
|
||||||
if (!inputManifestFiles || inputManifestFiles.length === 0) {
|
if (!inputManifestFiles || inputManifestFiles.length === 0) {
|
||||||
throw new Error(`ManifestFileNotFound : ${manifestFilePaths}`);
|
throw new Error(`ManifestFileNotFound : ${manifestFilePaths}`);
|
||||||
}
|
}
|
||||||
// artifact substitution
|
// artifact substitution
|
||||||
inputManifestFiles = updateContainerImagesInManifestFiles(inputManifestFiles, TaskInputParameters.containers);
|
inputManifestFiles = updateContainerImagesInManifestFiles(inputManifestFiles, TaskInputParameters.containers);
|
||||||
// imagePullSecrets addition
|
// imagePullSecrets addition
|
||||||
inputManifestFiles = updateImagePullSecretsInManifestFiles(inputManifestFiles, TaskInputParameters.imagePullSecrets);
|
inputManifestFiles = updateImagePullSecretsInManifestFiles(inputManifestFiles, TaskInputParameters.imagePullSecrets);
|
||||||
return inputManifestFiles;
|
return inputManifestFiles;
|
||||||
}
|
}
|
||||||
exports.getUpdatedManifestFiles = getUpdatedManifestFiles;
|
exports.getUpdatedManifestFiles = getUpdatedManifestFiles;
|
||||||
const workloadTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
|
const workloadTypes = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
|
||||||
function isWorkloadEntity(kind) {
|
function isWorkloadEntity(kind) {
|
||||||
if (!kind) {
|
if (!kind) {
|
||||||
core.debug('ResourceKindNotDefined');
|
core.debug('ResourceKindNotDefined');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return workloadTypes.some((type) => {
|
return workloadTypes.some((type) => {
|
||||||
return utility_1.isEqual(type, kind);
|
return utility_1.isEqual(type, kind);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.isWorkloadEntity = isWorkloadEntity;
|
exports.isWorkloadEntity = isWorkloadEntity;
|
||||||
|
|||||||
@@ -1,277 +1,277 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.getResources = exports.updateSelectorLabels = exports.updateSpecLabels = exports.updateImagePullSecrets = exports.updateObjectAnnotations = exports.updateObjectLabels = exports.getReplicaCount = exports.isIngressEntity = exports.isServiceEntity = exports.isWorkloadEntity = exports.isDeploymentEntity = void 0;
|
exports.getResources = exports.updateSelectorLabels = exports.updateSpecLabels = exports.updateImagePullSecrets = exports.updateObjectAnnotations = exports.updateObjectLabels = exports.getReplicaCount = exports.isIngressEntity = exports.isServiceEntity = exports.isWorkloadEntity = exports.isDeploymentEntity = void 0;
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const yaml = require("js-yaml");
|
const yaml = require("js-yaml");
|
||||||
const constants_1 = require("../constants");
|
const constants_1 = require("../constants");
|
||||||
const string_comparison_1 = require("./string-comparison");
|
const string_comparison_1 = require("./string-comparison");
|
||||||
const INGRESS = "Ingress";
|
const INGRESS = "Ingress";
|
||||||
function isDeploymentEntity(kind) {
|
function isDeploymentEntity(kind) {
|
||||||
if (!kind) {
|
if (!kind) {
|
||||||
throw ('ResourceKindNotDefined');
|
throw ('ResourceKindNotDefined');
|
||||||
}
|
}
|
||||||
return constants_1.deploymentTypes.some((type) => {
|
return constants_1.deploymentTypes.some((type) => {
|
||||||
return string_comparison_1.isEqual(type, kind, string_comparison_1.StringComparer.OrdinalIgnoreCase);
|
return string_comparison_1.isEqual(type, kind, string_comparison_1.StringComparer.OrdinalIgnoreCase);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.isDeploymentEntity = isDeploymentEntity;
|
exports.isDeploymentEntity = isDeploymentEntity;
|
||||||
function isWorkloadEntity(kind) {
|
function isWorkloadEntity(kind) {
|
||||||
if (!kind) {
|
if (!kind) {
|
||||||
throw ('ResourceKindNotDefined');
|
throw ('ResourceKindNotDefined');
|
||||||
}
|
}
|
||||||
return constants_1.workloadTypes.some((type) => {
|
return constants_1.workloadTypes.some((type) => {
|
||||||
return string_comparison_1.isEqual(type, kind, string_comparison_1.StringComparer.OrdinalIgnoreCase);
|
return string_comparison_1.isEqual(type, kind, string_comparison_1.StringComparer.OrdinalIgnoreCase);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.isWorkloadEntity = isWorkloadEntity;
|
exports.isWorkloadEntity = isWorkloadEntity;
|
||||||
function isServiceEntity(kind) {
|
function isServiceEntity(kind) {
|
||||||
if (!kind) {
|
if (!kind) {
|
||||||
throw ('ResourceKindNotDefined');
|
throw ('ResourceKindNotDefined');
|
||||||
}
|
}
|
||||||
return string_comparison_1.isEqual("Service", kind, string_comparison_1.StringComparer.OrdinalIgnoreCase);
|
return string_comparison_1.isEqual("Service", kind, string_comparison_1.StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
exports.isServiceEntity = isServiceEntity;
|
exports.isServiceEntity = isServiceEntity;
|
||||||
function isIngressEntity(kind) {
|
function isIngressEntity(kind) {
|
||||||
if (!kind) {
|
if (!kind) {
|
||||||
throw ('ResourceKindNotDefined');
|
throw ('ResourceKindNotDefined');
|
||||||
}
|
}
|
||||||
return string_comparison_1.isEqual(INGRESS, kind, string_comparison_1.StringComparer.OrdinalIgnoreCase);
|
return string_comparison_1.isEqual(INGRESS, kind, string_comparison_1.StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
exports.isIngressEntity = isIngressEntity;
|
exports.isIngressEntity = isIngressEntity;
|
||||||
function getReplicaCount(inputObject) {
|
function getReplicaCount(inputObject) {
|
||||||
if (!inputObject) {
|
if (!inputObject) {
|
||||||
throw ('NullInputObject');
|
throw ('NullInputObject');
|
||||||
}
|
}
|
||||||
if (!inputObject.kind) {
|
if (!inputObject.kind) {
|
||||||
throw ('ResourceKindNotDefined');
|
throw ('ResourceKindNotDefined');
|
||||||
}
|
}
|
||||||
const kind = inputObject.kind;
|
const kind = inputObject.kind;
|
||||||
if (!string_comparison_1.isEqual(kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase) && !string_comparison_1.isEqual(kind, constants_1.KubernetesWorkload.daemonSet, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
if (!string_comparison_1.isEqual(kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase) && !string_comparison_1.isEqual(kind, constants_1.KubernetesWorkload.daemonSet, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
||||||
return inputObject.spec.replicas;
|
return inputObject.spec.replicas;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
exports.getReplicaCount = getReplicaCount;
|
exports.getReplicaCount = getReplicaCount;
|
||||||
function updateObjectLabels(inputObject, newLabels, override) {
|
function updateObjectLabels(inputObject, newLabels, override) {
|
||||||
if (!inputObject) {
|
if (!inputObject) {
|
||||||
throw ('NullInputObject');
|
throw ('NullInputObject');
|
||||||
}
|
}
|
||||||
if (!inputObject.metadata) {
|
if (!inputObject.metadata) {
|
||||||
throw ('NullInputObjectMetadata');
|
throw ('NullInputObjectMetadata');
|
||||||
}
|
}
|
||||||
if (!newLabels) {
|
if (!newLabels) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (override) {
|
if (override) {
|
||||||
inputObject.metadata.labels = newLabels;
|
inputObject.metadata.labels = newLabels;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let existingLabels = inputObject.metadata.labels;
|
let existingLabels = inputObject.metadata.labels;
|
||||||
if (!existingLabels) {
|
if (!existingLabels) {
|
||||||
existingLabels = new Map();
|
existingLabels = new Map();
|
||||||
}
|
}
|
||||||
Object.keys(newLabels).forEach(function (key) {
|
Object.keys(newLabels).forEach(function (key) {
|
||||||
existingLabels[key] = newLabels[key];
|
existingLabels[key] = newLabels[key];
|
||||||
});
|
});
|
||||||
inputObject.metadata.labels = existingLabels;
|
inputObject.metadata.labels = existingLabels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.updateObjectLabels = updateObjectLabels;
|
exports.updateObjectLabels = updateObjectLabels;
|
||||||
function updateObjectAnnotations(inputObject, newAnnotations, override) {
|
function updateObjectAnnotations(inputObject, newAnnotations, override) {
|
||||||
if (!inputObject) {
|
if (!inputObject) {
|
||||||
throw ('NullInputObject');
|
throw ('NullInputObject');
|
||||||
}
|
}
|
||||||
if (!inputObject.metadata) {
|
if (!inputObject.metadata) {
|
||||||
throw ('NullInputObjectMetadata');
|
throw ('NullInputObjectMetadata');
|
||||||
}
|
}
|
||||||
if (!newAnnotations) {
|
if (!newAnnotations) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (override) {
|
if (override) {
|
||||||
inputObject.metadata.annotations = newAnnotations;
|
inputObject.metadata.annotations = newAnnotations;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let existingAnnotations = inputObject.metadata.annotations;
|
let existingAnnotations = inputObject.metadata.annotations;
|
||||||
if (!existingAnnotations) {
|
if (!existingAnnotations) {
|
||||||
existingAnnotations = new Map();
|
existingAnnotations = new Map();
|
||||||
}
|
}
|
||||||
Object.keys(newAnnotations).forEach(function (key) {
|
Object.keys(newAnnotations).forEach(function (key) {
|
||||||
existingAnnotations[key] = newAnnotations[key];
|
existingAnnotations[key] = newAnnotations[key];
|
||||||
});
|
});
|
||||||
inputObject.metadata.annotations = existingAnnotations;
|
inputObject.metadata.annotations = existingAnnotations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.updateObjectAnnotations = updateObjectAnnotations;
|
exports.updateObjectAnnotations = updateObjectAnnotations;
|
||||||
function updateImagePullSecrets(inputObject, newImagePullSecrets, override) {
|
function updateImagePullSecrets(inputObject, newImagePullSecrets, override) {
|
||||||
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const newImagePullSecretsObjects = Array.from(newImagePullSecrets, x => { return { 'name': x }; });
|
const newImagePullSecretsObjects = Array.from(newImagePullSecrets, x => { return { 'name': x }; });
|
||||||
let existingImagePullSecretObjects = getImagePullSecrets(inputObject);
|
let existingImagePullSecretObjects = getImagePullSecrets(inputObject);
|
||||||
if (override) {
|
if (override) {
|
||||||
existingImagePullSecretObjects = newImagePullSecretsObjects;
|
existingImagePullSecretObjects = newImagePullSecretsObjects;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!existingImagePullSecretObjects) {
|
if (!existingImagePullSecretObjects) {
|
||||||
existingImagePullSecretObjects = new Array();
|
existingImagePullSecretObjects = new Array();
|
||||||
}
|
}
|
||||||
existingImagePullSecretObjects = existingImagePullSecretObjects.concat(newImagePullSecretsObjects);
|
existingImagePullSecretObjects = existingImagePullSecretObjects.concat(newImagePullSecretsObjects);
|
||||||
}
|
}
|
||||||
setImagePullSecrets(inputObject, existingImagePullSecretObjects);
|
setImagePullSecrets(inputObject, existingImagePullSecretObjects);
|
||||||
}
|
}
|
||||||
exports.updateImagePullSecrets = updateImagePullSecrets;
|
exports.updateImagePullSecrets = updateImagePullSecrets;
|
||||||
function updateSpecLabels(inputObject, newLabels, override) {
|
function updateSpecLabels(inputObject, newLabels, override) {
|
||||||
if (!inputObject) {
|
if (!inputObject) {
|
||||||
throw ('NullInputObject');
|
throw ('NullInputObject');
|
||||||
}
|
}
|
||||||
if (!inputObject.kind) {
|
if (!inputObject.kind) {
|
||||||
throw ('ResourceKindNotDefined');
|
throw ('ResourceKindNotDefined');
|
||||||
}
|
}
|
||||||
if (!newLabels) {
|
if (!newLabels) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let existingLabels = getSpecLabels(inputObject);
|
let existingLabels = getSpecLabels(inputObject);
|
||||||
if (override) {
|
if (override) {
|
||||||
existingLabels = newLabels;
|
existingLabels = newLabels;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!existingLabels) {
|
if (!existingLabels) {
|
||||||
existingLabels = new Map();
|
existingLabels = new Map();
|
||||||
}
|
}
|
||||||
Object.keys(newLabels).forEach(function (key) {
|
Object.keys(newLabels).forEach(function (key) {
|
||||||
existingLabels[key] = newLabels[key];
|
existingLabels[key] = newLabels[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setSpecLabels(inputObject, existingLabels);
|
setSpecLabels(inputObject, existingLabels);
|
||||||
}
|
}
|
||||||
exports.updateSpecLabels = updateSpecLabels;
|
exports.updateSpecLabels = updateSpecLabels;
|
||||||
function updateSelectorLabels(inputObject, newLabels, override) {
|
function updateSelectorLabels(inputObject, newLabels, override) {
|
||||||
if (!inputObject) {
|
if (!inputObject) {
|
||||||
throw ('NullInputObject');
|
throw ('NullInputObject');
|
||||||
}
|
}
|
||||||
if (!inputObject.kind) {
|
if (!inputObject.kind) {
|
||||||
throw ('ResourceKindNotDefined');
|
throw ('ResourceKindNotDefined');
|
||||||
}
|
}
|
||||||
if (!newLabels) {
|
if (!newLabels) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let existingLabels = getSpecSelectorLabels(inputObject);
|
let existingLabels = getSpecSelectorLabels(inputObject);
|
||||||
if (override) {
|
if (override) {
|
||||||
existingLabels = newLabels;
|
existingLabels = newLabels;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!existingLabels) {
|
if (!existingLabels) {
|
||||||
existingLabels = new Map();
|
existingLabels = new Map();
|
||||||
}
|
}
|
||||||
Object.keys(newLabels).forEach(function (key) {
|
Object.keys(newLabels).forEach(function (key) {
|
||||||
existingLabels[key] = newLabels[key];
|
existingLabels[key] = newLabels[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setSpecSelectorLabels(inputObject, existingLabels);
|
setSpecSelectorLabels(inputObject, existingLabels);
|
||||||
}
|
}
|
||||||
exports.updateSelectorLabels = updateSelectorLabels;
|
exports.updateSelectorLabels = updateSelectorLabels;
|
||||||
function getResources(filePaths, filterResourceTypes) {
|
function getResources(filePaths, filterResourceTypes) {
|
||||||
if (!filePaths) {
|
if (!filePaths) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const resources = [];
|
const resources = [];
|
||||||
filePaths.forEach((filePath) => {
|
filePaths.forEach((filePath) => {
|
||||||
const fileContents = fs.readFileSync(filePath);
|
const fileContents = fs.readFileSync(filePath);
|
||||||
yaml.safeLoadAll(fileContents, function (inputObject) {
|
yaml.safeLoadAll(fileContents, function (inputObject) {
|
||||||
const inputObjectKind = inputObject ? inputObject.kind : '';
|
const inputObjectKind = inputObject ? inputObject.kind : '';
|
||||||
if (filterResourceTypes.filter(type => string_comparison_1.isEqual(inputObjectKind, type, string_comparison_1.StringComparer.OrdinalIgnoreCase)).length > 0) {
|
if (filterResourceTypes.filter(type => string_comparison_1.isEqual(inputObjectKind, type, string_comparison_1.StringComparer.OrdinalIgnoreCase)).length > 0) {
|
||||||
const resource = {
|
const resource = {
|
||||||
type: inputObject.kind,
|
type: inputObject.kind,
|
||||||
name: inputObject.metadata.name
|
name: inputObject.metadata.name
|
||||||
};
|
};
|
||||||
resources.push(resource);
|
resources.push(resource);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
exports.getResources = getResources;
|
exports.getResources = getResources;
|
||||||
function getSpecLabels(inputObject) {
|
function getSpecLabels(inputObject) {
|
||||||
if (!inputObject) {
|
if (!inputObject) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
||||||
return inputObject.metadata.labels;
|
return inputObject.metadata.labels;
|
||||||
}
|
}
|
||||||
if (!!inputObject.spec && !!inputObject.spec.template && !!inputObject.spec.template.metadata) {
|
if (!!inputObject.spec && !!inputObject.spec.template && !!inputObject.spec.template.metadata) {
|
||||||
return inputObject.spec.template.metadata.labels;
|
return inputObject.spec.template.metadata.labels;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
function getImagePullSecrets(inputObject) {
|
function getImagePullSecrets(inputObject) {
|
||||||
if (!inputObject || !inputObject.spec) {
|
if (!inputObject || !inputObject.spec) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.cronjob, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.cronjob, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
||||||
try {
|
try {
|
||||||
return inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets;
|
return inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets;
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.debug(`Fetching imagePullSecrets failed due to this error: ${JSON.stringify(ex)}`);
|
core.debug(`Fetching imagePullSecrets failed due to this error: ${JSON.stringify(ex)}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
||||||
return inputObject.spec.imagePullSecrets;
|
return inputObject.spec.imagePullSecrets;
|
||||||
}
|
}
|
||||||
if (!!inputObject.spec.template && !!inputObject.spec.template.spec) {
|
if (!!inputObject.spec.template && !!inputObject.spec.template.spec) {
|
||||||
return inputObject.spec.template.spec.imagePullSecrets;
|
return inputObject.spec.template.spec.imagePullSecrets;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
function setImagePullSecrets(inputObject, newImagePullSecrets) {
|
function setImagePullSecrets(inputObject, newImagePullSecrets) {
|
||||||
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
if (!inputObject || !inputObject.spec || !newImagePullSecrets) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
||||||
inputObject.spec.imagePullSecrets = newImagePullSecrets;
|
inputObject.spec.imagePullSecrets = newImagePullSecrets;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.cronjob, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
if (string_comparison_1.isEqual(inputObject.kind, constants_1.KubernetesWorkload.cronjob, string_comparison_1.StringComparer.OrdinalIgnoreCase)) {
|
||||||
try {
|
try {
|
||||||
inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
inputObject.spec.jobTemplate.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.debug(`Overriding imagePullSecrets failed due to this error: ${JSON.stringify(ex)}`);
|
core.debug(`Overriding imagePullSecrets failed due to this error: ${JSON.stringify(ex)}`);
|
||||||
//Do nothing
|
//Do nothing
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!!inputObject.spec.template && !!inputObject.spec.template.spec) {
|
if (!!inputObject.spec.template && !!inputObject.spec.template.spec) {
|
||||||
inputObject.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
inputObject.spec.template.spec.imagePullSecrets = newImagePullSecrets;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
function setSpecLabels(inputObject, newLabels) {
|
function setSpecLabels(inputObject, newLabels) {
|
||||||
let specLabels = getSpecLabels(inputObject);
|
let specLabels = getSpecLabels(inputObject);
|
||||||
if (!!newLabels) {
|
if (!!newLabels) {
|
||||||
specLabels = newLabels;
|
specLabels = newLabels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getSpecSelectorLabels(inputObject) {
|
function getSpecSelectorLabels(inputObject) {
|
||||||
if (!!inputObject && !!inputObject.spec && !!inputObject.spec.selector) {
|
if (!!inputObject && !!inputObject.spec && !!inputObject.spec.selector) {
|
||||||
if (isServiceEntity(inputObject.kind)) {
|
if (isServiceEntity(inputObject.kind)) {
|
||||||
return inputObject.spec.selector;
|
return inputObject.spec.selector;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return inputObject.spec.selector.matchLabels;
|
return inputObject.spec.selector.matchLabels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
function setSpecSelectorLabels(inputObject, newLabels) {
|
function setSpecSelectorLabels(inputObject, newLabels) {
|
||||||
let selectorLabels = getSpecSelectorLabels(inputObject);
|
let selectorLabels = getSpecSelectorLabels(inputObject);
|
||||||
if (!!selectorLabels) {
|
if (!!selectorLabels) {
|
||||||
selectorLabels = newLabels;
|
selectorLabels = newLabels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,187 +1,187 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.getStableResourceName = exports.getBaselineResourceName = exports.getCanaryResourceName = exports.isSMICanaryStrategy = exports.isCanaryDeploymentStrategy = exports.fetchResource = exports.fetchCanaryResource = exports.getNewCanaryResource = exports.getNewBaselineResource = exports.getStableResource = exports.isResourceMarkedAsStable = exports.markResourceAsStable = exports.deleteCanaryDeployment = exports.STABLE_LABEL_VALUE = exports.STABLE_SUFFIX = exports.CANARY_LABEL_VALUE = exports.BASELINE_LABEL_VALUE = exports.CANARY_VERSION_LABEL = exports.TRAFFIC_SPLIT_STRATEGY = exports.CANARY_DEPLOYMENT_STRATEGY = void 0;
|
exports.getStableResourceName = exports.getBaselineResourceName = exports.getCanaryResourceName = exports.isSMICanaryStrategy = exports.isCanaryDeploymentStrategy = exports.fetchResource = exports.fetchCanaryResource = exports.getNewCanaryResource = exports.getNewBaselineResource = exports.getStableResource = exports.isResourceMarkedAsStable = exports.markResourceAsStable = exports.deleteCanaryDeployment = exports.STABLE_LABEL_VALUE = exports.STABLE_SUFFIX = exports.CANARY_LABEL_VALUE = exports.BASELINE_LABEL_VALUE = exports.CANARY_VERSION_LABEL = exports.TRAFFIC_SPLIT_STRATEGY = exports.CANARY_DEPLOYMENT_STRATEGY = void 0;
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const yaml = require("js-yaml");
|
const yaml = require("js-yaml");
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const TaskInputParameters = require("../../input-parameters");
|
const TaskInputParameters = require("../../input-parameters");
|
||||||
const helper = require("../resource-object-utility");
|
const helper = require("../resource-object-utility");
|
||||||
const constants_1 = require("../../constants");
|
const constants_1 = require("../../constants");
|
||||||
const string_comparison_1 = require("../string-comparison");
|
const string_comparison_1 = require("../string-comparison");
|
||||||
const utility_1 = require("../utility");
|
const utility_1 = require("../utility");
|
||||||
const utils = require("../manifest-utilities");
|
const utils = require("../manifest-utilities");
|
||||||
exports.CANARY_DEPLOYMENT_STRATEGY = 'CANARY';
|
exports.CANARY_DEPLOYMENT_STRATEGY = 'CANARY';
|
||||||
exports.TRAFFIC_SPLIT_STRATEGY = 'SMI';
|
exports.TRAFFIC_SPLIT_STRATEGY = 'SMI';
|
||||||
exports.CANARY_VERSION_LABEL = 'workflow/version';
|
exports.CANARY_VERSION_LABEL = 'workflow/version';
|
||||||
const BASELINE_SUFFIX = '-baseline';
|
const BASELINE_SUFFIX = '-baseline';
|
||||||
exports.BASELINE_LABEL_VALUE = 'baseline';
|
exports.BASELINE_LABEL_VALUE = 'baseline';
|
||||||
const CANARY_SUFFIX = '-canary';
|
const CANARY_SUFFIX = '-canary';
|
||||||
exports.CANARY_LABEL_VALUE = 'canary';
|
exports.CANARY_LABEL_VALUE = 'canary';
|
||||||
exports.STABLE_SUFFIX = '-stable';
|
exports.STABLE_SUFFIX = '-stable';
|
||||||
exports.STABLE_LABEL_VALUE = 'stable';
|
exports.STABLE_LABEL_VALUE = 'stable';
|
||||||
function deleteCanaryDeployment(kubectl, manifestFilePaths, includeServices) {
|
function deleteCanaryDeployment(kubectl, manifestFilePaths, includeServices) {
|
||||||
// get manifest files
|
// get manifest files
|
||||||
const inputManifestFiles = utils.getManifestFiles(manifestFilePaths);
|
const inputManifestFiles = utils.getManifestFiles(manifestFilePaths);
|
||||||
if (inputManifestFiles == null || inputManifestFiles.length == 0) {
|
if (inputManifestFiles == null || inputManifestFiles.length == 0) {
|
||||||
throw new Error('ManifestFileNotFound');
|
throw new Error('ManifestFileNotFound');
|
||||||
}
|
}
|
||||||
// create delete cmd prefix
|
// create delete cmd prefix
|
||||||
cleanUpCanary(kubectl, inputManifestFiles, includeServices);
|
cleanUpCanary(kubectl, inputManifestFiles, includeServices);
|
||||||
}
|
}
|
||||||
exports.deleteCanaryDeployment = deleteCanaryDeployment;
|
exports.deleteCanaryDeployment = deleteCanaryDeployment;
|
||||||
function markResourceAsStable(inputObject) {
|
function markResourceAsStable(inputObject) {
|
||||||
if (isResourceMarkedAsStable(inputObject)) {
|
if (isResourceMarkedAsStable(inputObject)) {
|
||||||
return inputObject;
|
return inputObject;
|
||||||
}
|
}
|
||||||
const newObject = JSON.parse(JSON.stringify(inputObject));
|
const newObject = JSON.parse(JSON.stringify(inputObject));
|
||||||
// Adding labels and annotations.
|
// Adding labels and annotations.
|
||||||
addCanaryLabelsAndAnnotations(newObject, exports.STABLE_LABEL_VALUE);
|
addCanaryLabelsAndAnnotations(newObject, exports.STABLE_LABEL_VALUE);
|
||||||
core.debug("Added stable label: " + JSON.stringify(newObject));
|
core.debug("Added stable label: " + JSON.stringify(newObject));
|
||||||
return newObject;
|
return newObject;
|
||||||
}
|
}
|
||||||
exports.markResourceAsStable = markResourceAsStable;
|
exports.markResourceAsStable = markResourceAsStable;
|
||||||
function isResourceMarkedAsStable(inputObject) {
|
function isResourceMarkedAsStable(inputObject) {
|
||||||
return inputObject &&
|
return inputObject &&
|
||||||
inputObject.metadata &&
|
inputObject.metadata &&
|
||||||
inputObject.metadata.labels &&
|
inputObject.metadata.labels &&
|
||||||
inputObject.metadata.labels[exports.CANARY_VERSION_LABEL] == exports.STABLE_LABEL_VALUE;
|
inputObject.metadata.labels[exports.CANARY_VERSION_LABEL] == exports.STABLE_LABEL_VALUE;
|
||||||
}
|
}
|
||||||
exports.isResourceMarkedAsStable = isResourceMarkedAsStable;
|
exports.isResourceMarkedAsStable = isResourceMarkedAsStable;
|
||||||
function getStableResource(inputObject) {
|
function getStableResource(inputObject) {
|
||||||
var replicaCount = isSpecContainsReplicas(inputObject.kind) ? inputObject.metadata.replicas : 0;
|
var replicaCount = isSpecContainsReplicas(inputObject.kind) ? inputObject.metadata.replicas : 0;
|
||||||
return getNewCanaryObject(inputObject, replicaCount, exports.STABLE_LABEL_VALUE);
|
return getNewCanaryObject(inputObject, replicaCount, exports.STABLE_LABEL_VALUE);
|
||||||
}
|
}
|
||||||
exports.getStableResource = getStableResource;
|
exports.getStableResource = getStableResource;
|
||||||
function getNewBaselineResource(stableObject, replicas) {
|
function getNewBaselineResource(stableObject, replicas) {
|
||||||
return getNewCanaryObject(stableObject, replicas, exports.BASELINE_LABEL_VALUE);
|
return getNewCanaryObject(stableObject, replicas, exports.BASELINE_LABEL_VALUE);
|
||||||
}
|
}
|
||||||
exports.getNewBaselineResource = getNewBaselineResource;
|
exports.getNewBaselineResource = getNewBaselineResource;
|
||||||
function getNewCanaryResource(inputObject, replicas) {
|
function getNewCanaryResource(inputObject, replicas) {
|
||||||
return getNewCanaryObject(inputObject, replicas, exports.CANARY_LABEL_VALUE);
|
return getNewCanaryObject(inputObject, replicas, exports.CANARY_LABEL_VALUE);
|
||||||
}
|
}
|
||||||
exports.getNewCanaryResource = getNewCanaryResource;
|
exports.getNewCanaryResource = getNewCanaryResource;
|
||||||
function fetchCanaryResource(kubectl, kind, name) {
|
function fetchCanaryResource(kubectl, kind, name) {
|
||||||
return fetchResource(kubectl, kind, getCanaryResourceName(name));
|
return fetchResource(kubectl, kind, getCanaryResourceName(name));
|
||||||
}
|
}
|
||||||
exports.fetchCanaryResource = fetchCanaryResource;
|
exports.fetchCanaryResource = fetchCanaryResource;
|
||||||
function fetchResource(kubectl, kind, name) {
|
function fetchResource(kubectl, kind, name) {
|
||||||
const result = kubectl.getResource(kind, name);
|
const result = kubectl.getResource(kind, name);
|
||||||
if (result == null || !!result.stderr) {
|
if (result == null || !!result.stderr) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!!result.stdout) {
|
if (!!result.stdout) {
|
||||||
const resource = JSON.parse(result.stdout);
|
const resource = JSON.parse(result.stdout);
|
||||||
try {
|
try {
|
||||||
UnsetsClusterSpecficDetails(resource);
|
UnsetsClusterSpecficDetails(resource);
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
core.debug('Exception occurred while Parsing ' + resource + ' in Json object');
|
core.debug('Exception occurred while Parsing ' + resource + ' in Json object');
|
||||||
core.debug(`Exception:${ex}`);
|
core.debug(`Exception:${ex}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
exports.fetchResource = fetchResource;
|
exports.fetchResource = fetchResource;
|
||||||
function isCanaryDeploymentStrategy() {
|
function isCanaryDeploymentStrategy() {
|
||||||
const deploymentStrategy = TaskInputParameters.deploymentStrategy;
|
const deploymentStrategy = TaskInputParameters.deploymentStrategy;
|
||||||
return deploymentStrategy && deploymentStrategy.toUpperCase() === exports.CANARY_DEPLOYMENT_STRATEGY;
|
return deploymentStrategy && deploymentStrategy.toUpperCase() === exports.CANARY_DEPLOYMENT_STRATEGY;
|
||||||
}
|
}
|
||||||
exports.isCanaryDeploymentStrategy = isCanaryDeploymentStrategy;
|
exports.isCanaryDeploymentStrategy = isCanaryDeploymentStrategy;
|
||||||
function isSMICanaryStrategy() {
|
function isSMICanaryStrategy() {
|
||||||
const deploymentStrategy = TaskInputParameters.trafficSplitMethod;
|
const deploymentStrategy = TaskInputParameters.trafficSplitMethod;
|
||||||
return isCanaryDeploymentStrategy() && deploymentStrategy && deploymentStrategy.toUpperCase() === exports.TRAFFIC_SPLIT_STRATEGY;
|
return isCanaryDeploymentStrategy() && deploymentStrategy && deploymentStrategy.toUpperCase() === exports.TRAFFIC_SPLIT_STRATEGY;
|
||||||
}
|
}
|
||||||
exports.isSMICanaryStrategy = isSMICanaryStrategy;
|
exports.isSMICanaryStrategy = isSMICanaryStrategy;
|
||||||
function getCanaryResourceName(name) {
|
function getCanaryResourceName(name) {
|
||||||
return name + CANARY_SUFFIX;
|
return name + CANARY_SUFFIX;
|
||||||
}
|
}
|
||||||
exports.getCanaryResourceName = getCanaryResourceName;
|
exports.getCanaryResourceName = getCanaryResourceName;
|
||||||
function getBaselineResourceName(name) {
|
function getBaselineResourceName(name) {
|
||||||
return name + BASELINE_SUFFIX;
|
return name + BASELINE_SUFFIX;
|
||||||
}
|
}
|
||||||
exports.getBaselineResourceName = getBaselineResourceName;
|
exports.getBaselineResourceName = getBaselineResourceName;
|
||||||
function getStableResourceName(name) {
|
function getStableResourceName(name) {
|
||||||
return name + exports.STABLE_SUFFIX;
|
return name + exports.STABLE_SUFFIX;
|
||||||
}
|
}
|
||||||
exports.getStableResourceName = getStableResourceName;
|
exports.getStableResourceName = getStableResourceName;
|
||||||
function UnsetsClusterSpecficDetails(resource) {
|
function UnsetsClusterSpecficDetails(resource) {
|
||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Unsets the cluster specific details in the object
|
// Unsets the cluster specific details in the object
|
||||||
if (!!resource) {
|
if (!!resource) {
|
||||||
const metadata = resource.metadata;
|
const metadata = resource.metadata;
|
||||||
const status = resource.status;
|
const status = resource.status;
|
||||||
if (!!metadata) {
|
if (!!metadata) {
|
||||||
const newMetadata = {
|
const newMetadata = {
|
||||||
'annotations': metadata.annotations,
|
'annotations': metadata.annotations,
|
||||||
'labels': metadata.labels,
|
'labels': metadata.labels,
|
||||||
'name': metadata.name
|
'name': metadata.name
|
||||||
};
|
};
|
||||||
resource.metadata = newMetadata;
|
resource.metadata = newMetadata;
|
||||||
}
|
}
|
||||||
if (!!status) {
|
if (!!status) {
|
||||||
resource.status = {};
|
resource.status = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function getNewCanaryObject(inputObject, replicas, type) {
|
function getNewCanaryObject(inputObject, replicas, type) {
|
||||||
const newObject = JSON.parse(JSON.stringify(inputObject));
|
const newObject = JSON.parse(JSON.stringify(inputObject));
|
||||||
// Updating name
|
// Updating name
|
||||||
if (type === exports.CANARY_LABEL_VALUE) {
|
if (type === exports.CANARY_LABEL_VALUE) {
|
||||||
newObject.metadata.name = getCanaryResourceName(inputObject.metadata.name);
|
newObject.metadata.name = getCanaryResourceName(inputObject.metadata.name);
|
||||||
}
|
}
|
||||||
else if (type === exports.STABLE_LABEL_VALUE) {
|
else if (type === exports.STABLE_LABEL_VALUE) {
|
||||||
newObject.metadata.name = getStableResourceName(inputObject.metadata.name);
|
newObject.metadata.name = getStableResourceName(inputObject.metadata.name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
newObject.metadata.name = getBaselineResourceName(inputObject.metadata.name);
|
newObject.metadata.name = getBaselineResourceName(inputObject.metadata.name);
|
||||||
}
|
}
|
||||||
// Adding labels and annotations.
|
// Adding labels and annotations.
|
||||||
addCanaryLabelsAndAnnotations(newObject, type);
|
addCanaryLabelsAndAnnotations(newObject, type);
|
||||||
// Updating no. of replicas
|
// Updating no. of replicas
|
||||||
if (isSpecContainsReplicas(newObject.kind)) {
|
if (isSpecContainsReplicas(newObject.kind)) {
|
||||||
newObject.spec.replicas = replicas;
|
newObject.spec.replicas = replicas;
|
||||||
}
|
}
|
||||||
return newObject;
|
return newObject;
|
||||||
}
|
}
|
||||||
function isSpecContainsReplicas(kind) {
|
function isSpecContainsReplicas(kind) {
|
||||||
return !string_comparison_1.isEqual(kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase) &&
|
return !string_comparison_1.isEqual(kind, constants_1.KubernetesWorkload.pod, string_comparison_1.StringComparer.OrdinalIgnoreCase) &&
|
||||||
!string_comparison_1.isEqual(kind, constants_1.KubernetesWorkload.daemonSet, string_comparison_1.StringComparer.OrdinalIgnoreCase) &&
|
!string_comparison_1.isEqual(kind, constants_1.KubernetesWorkload.daemonSet, string_comparison_1.StringComparer.OrdinalIgnoreCase) &&
|
||||||
!helper.isServiceEntity(kind);
|
!helper.isServiceEntity(kind);
|
||||||
}
|
}
|
||||||
function addCanaryLabelsAndAnnotations(inputObject, type) {
|
function addCanaryLabelsAndAnnotations(inputObject, type) {
|
||||||
const newLabels = new Map();
|
const newLabels = new Map();
|
||||||
newLabels[exports.CANARY_VERSION_LABEL] = type;
|
newLabels[exports.CANARY_VERSION_LABEL] = type;
|
||||||
helper.updateObjectLabels(inputObject, newLabels, false);
|
helper.updateObjectLabels(inputObject, newLabels, false);
|
||||||
helper.updateObjectAnnotations(inputObject, newLabels, false);
|
helper.updateObjectAnnotations(inputObject, newLabels, false);
|
||||||
helper.updateSelectorLabels(inputObject, newLabels, false);
|
helper.updateSelectorLabels(inputObject, newLabels, false);
|
||||||
if (!helper.isServiceEntity(inputObject.kind)) {
|
if (!helper.isServiceEntity(inputObject.kind)) {
|
||||||
helper.updateSpecLabels(inputObject, newLabels, false);
|
helper.updateSpecLabels(inputObject, newLabels, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function cleanUpCanary(kubectl, files, includeServices) {
|
function cleanUpCanary(kubectl, files, includeServices) {
|
||||||
var deleteObject = function (kind, name) {
|
var deleteObject = function (kind, name) {
|
||||||
try {
|
try {
|
||||||
const result = kubectl.delete([kind, name]);
|
const result = kubectl.delete([kind, name]);
|
||||||
utility_1.checkForErrors([result]);
|
utility_1.checkForErrors([result]);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
// Ignore failures of delete if doesn't exist
|
// Ignore failures of delete if doesn't exist
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
files.forEach((filePath) => {
|
files.forEach((filePath) => {
|
||||||
const fileContents = fs.readFileSync(filePath);
|
const fileContents = fs.readFileSync(filePath);
|
||||||
yaml.safeLoadAll(fileContents, function (inputObject) {
|
yaml.safeLoadAll(fileContents, function (inputObject) {
|
||||||
const name = inputObject.metadata.name;
|
const name = inputObject.metadata.name;
|
||||||
const kind = inputObject.kind;
|
const kind = inputObject.kind;
|
||||||
if (helper.isDeploymentEntity(kind) || (includeServices && helper.isServiceEntity(kind))) {
|
if (helper.isDeploymentEntity(kind) || (includeServices && helper.isServiceEntity(kind))) {
|
||||||
const canaryObjectName = getCanaryResourceName(name);
|
const canaryObjectName = getCanaryResourceName(name);
|
||||||
const baselineObjectName = getBaselineResourceName(name);
|
const baselineObjectName = getBaselineResourceName(name);
|
||||||
deleteObject(kind, canaryObjectName);
|
deleteObject(kind, canaryObjectName);
|
||||||
deleteObject(kind, baselineObjectName);
|
deleteObject(kind, baselineObjectName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,148 +1,163 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
return new (P || (P = Promise))(function (resolve, reject) {
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.getManifestFiles = exports.deploy = void 0;
|
exports.getManifestFiles = exports.deploy = void 0;
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const yaml = require("js-yaml");
|
const yaml = require("js-yaml");
|
||||||
const canaryDeploymentHelper = require("./canary-deployment-helper");
|
const canaryDeploymentHelper = require("./canary-deployment-helper");
|
||||||
const KubernetesObjectUtility = require("../resource-object-utility");
|
const KubernetesObjectUtility = require("../resource-object-utility");
|
||||||
const TaskInputParameters = require("../../input-parameters");
|
const TaskInputParameters = require("../../input-parameters");
|
||||||
const models = require("../../constants");
|
const models = require("../../constants");
|
||||||
const fileHelper = require("../files-helper");
|
const fileHelper = require("../files-helper");
|
||||||
const utils = require("../manifest-utilities");
|
const utils = require("../manifest-utilities");
|
||||||
const KubernetesManifestUtility = require("../manifest-stability-utility");
|
const KubernetesManifestUtility = require("../manifest-stability-utility");
|
||||||
const KubernetesConstants = require("../../constants");
|
const KubernetesConstants = require("../../constants");
|
||||||
const manifest_utilities_1 = require("../manifest-utilities");
|
const manifest_utilities_1 = require("../manifest-utilities");
|
||||||
const pod_canary_deployment_helper_1 = require("./pod-canary-deployment-helper");
|
const pod_canary_deployment_helper_1 = require("./pod-canary-deployment-helper");
|
||||||
const smi_canary_deployment_helper_1 = require("./smi-canary-deployment-helper");
|
const smi_canary_deployment_helper_1 = require("./smi-canary-deployment-helper");
|
||||||
const utility_1 = require("../utility");
|
const utility_1 = require("../utility");
|
||||||
const blue_green_helper_1 = require("./blue-green-helper");
|
const blue_green_helper_1 = require("./blue-green-helper");
|
||||||
const service_blue_green_helper_1 = require("./service-blue-green-helper");
|
const service_blue_green_helper_1 = require("./service-blue-green-helper");
|
||||||
const ingress_blue_green_helper_1 = require("./ingress-blue-green-helper");
|
const ingress_blue_green_helper_1 = require("./ingress-blue-green-helper");
|
||||||
const smi_blue_green_helper_1 = require("./smi-blue-green-helper");
|
const smi_blue_green_helper_1 = require("./smi-blue-green-helper");
|
||||||
function deploy(kubectl, manifestFilePaths, deploymentStrategy) {
|
function deploy(kubectl, manifestFilePaths, deploymentStrategy) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
// get manifest files
|
// get manifest files
|
||||||
let inputManifestFiles = manifest_utilities_1.getUpdatedManifestFiles(manifestFilePaths);
|
let inputManifestFiles = manifest_utilities_1.getUpdatedManifestFiles(manifestFilePaths);
|
||||||
// deployment
|
// deployment
|
||||||
const deployedManifestFiles = deployManifests(inputManifestFiles, kubectl, isCanaryDeploymentStrategy(deploymentStrategy), blue_green_helper_1.isBlueGreenDeploymentStrategy());
|
const deployedManifestFiles = deployManifests(inputManifestFiles, kubectl, isCanaryDeploymentStrategy(deploymentStrategy), blue_green_helper_1.isBlueGreenDeploymentStrategy());
|
||||||
// check manifest stability
|
// check manifest stability
|
||||||
const resourceTypes = KubernetesObjectUtility.getResources(deployedManifestFiles, models.deploymentTypes.concat([KubernetesConstants.DiscoveryAndLoadBalancerResource.service]));
|
const resourceTypes = KubernetesObjectUtility.getResources(deployedManifestFiles, models.deploymentTypes.concat([KubernetesConstants.DiscoveryAndLoadBalancerResource.service]));
|
||||||
yield checkManifestStability(kubectl, resourceTypes);
|
yield checkManifestStability(kubectl, resourceTypes);
|
||||||
// route blue-green deployments
|
// route blue-green deployments
|
||||||
if (blue_green_helper_1.isBlueGreenDeploymentStrategy()) {
|
if (blue_green_helper_1.isBlueGreenDeploymentStrategy()) {
|
||||||
yield blue_green_helper_1.routeBlueGreen(kubectl, inputManifestFiles);
|
yield blue_green_helper_1.routeBlueGreen(kubectl, inputManifestFiles);
|
||||||
}
|
}
|
||||||
// print ingress resources
|
// print ingress resources
|
||||||
const ingressResources = KubernetesObjectUtility.getResources(deployedManifestFiles, [KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress]);
|
const ingressResources = KubernetesObjectUtility.getResources(deployedManifestFiles, [KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress]);
|
||||||
ingressResources.forEach(ingressResource => {
|
ingressResources.forEach(ingressResource => {
|
||||||
kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name);
|
kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name);
|
||||||
});
|
});
|
||||||
// annotate resources
|
// annotate resources
|
||||||
let allPods;
|
let allPods;
|
||||||
try {
|
try {
|
||||||
allPods = JSON.parse((kubectl.getAllPods()).stdout);
|
allPods = JSON.parse((kubectl.getAllPods()).stdout);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
core.debug("Unable to parse pods; Error: " + e);
|
core.debug("Unable to parse pods; Error: " + e);
|
||||||
}
|
}
|
||||||
annotateResources(deployedManifestFiles, kubectl, resourceTypes, allPods);
|
annotateAndLabelResources(deployedManifestFiles, kubectl, resourceTypes, allPods);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.deploy = deploy;
|
exports.deploy = deploy;
|
||||||
function getManifestFiles(manifestFilePaths) {
|
function getManifestFiles(manifestFilePaths) {
|
||||||
const files = utils.getManifestFiles(manifestFilePaths);
|
const files = utils.getManifestFiles(manifestFilePaths);
|
||||||
if (files == null || files.length === 0) {
|
if (files == null || files.length === 0) {
|
||||||
throw new Error(`ManifestFileNotFound : ${manifestFilePaths}`);
|
throw new Error(`ManifestFileNotFound : ${manifestFilePaths}`);
|
||||||
}
|
}
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
exports.getManifestFiles = getManifestFiles;
|
exports.getManifestFiles = getManifestFiles;
|
||||||
function deployManifests(files, kubectl, isCanaryDeploymentStrategy, isBlueGreenDeploymentStrategy) {
|
function deployManifests(files, kubectl, isCanaryDeploymentStrategy, isBlueGreenDeploymentStrategy) {
|
||||||
let result;
|
let result;
|
||||||
if (isCanaryDeploymentStrategy) {
|
if (isCanaryDeploymentStrategy) {
|
||||||
let canaryDeploymentOutput;
|
let canaryDeploymentOutput;
|
||||||
if (canaryDeploymentHelper.isSMICanaryStrategy()) {
|
if (canaryDeploymentHelper.isSMICanaryStrategy()) {
|
||||||
canaryDeploymentOutput = smi_canary_deployment_helper_1.deploySMICanary(kubectl, files);
|
canaryDeploymentOutput = smi_canary_deployment_helper_1.deploySMICanary(kubectl, files);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
canaryDeploymentOutput = pod_canary_deployment_helper_1.deployPodCanary(kubectl, files);
|
canaryDeploymentOutput = pod_canary_deployment_helper_1.deployPodCanary(kubectl, files);
|
||||||
}
|
}
|
||||||
result = canaryDeploymentOutput.result;
|
result = canaryDeploymentOutput.result;
|
||||||
files = canaryDeploymentOutput.newFilePaths;
|
files = canaryDeploymentOutput.newFilePaths;
|
||||||
}
|
}
|
||||||
else if (isBlueGreenDeploymentStrategy) {
|
else if (isBlueGreenDeploymentStrategy) {
|
||||||
let blueGreenDeploymentOutput;
|
let blueGreenDeploymentOutput;
|
||||||
if (blue_green_helper_1.isIngressRoute()) {
|
if (blue_green_helper_1.isIngressRoute()) {
|
||||||
blueGreenDeploymentOutput = ingress_blue_green_helper_1.deployBlueGreenIngress(kubectl, files);
|
blueGreenDeploymentOutput = ingress_blue_green_helper_1.deployBlueGreenIngress(kubectl, files);
|
||||||
}
|
}
|
||||||
else if (blue_green_helper_1.isSMIRoute()) {
|
else if (blue_green_helper_1.isSMIRoute()) {
|
||||||
blueGreenDeploymentOutput = smi_blue_green_helper_1.deployBlueGreenSMI(kubectl, files);
|
blueGreenDeploymentOutput = smi_blue_green_helper_1.deployBlueGreenSMI(kubectl, files);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
blueGreenDeploymentOutput = service_blue_green_helper_1.deployBlueGreenService(kubectl, files);
|
blueGreenDeploymentOutput = service_blue_green_helper_1.deployBlueGreenService(kubectl, files);
|
||||||
}
|
}
|
||||||
result = blueGreenDeploymentOutput.result;
|
result = blueGreenDeploymentOutput.result;
|
||||||
files = blueGreenDeploymentOutput.newFilePaths;
|
files = blueGreenDeploymentOutput.newFilePaths;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (canaryDeploymentHelper.isSMICanaryStrategy()) {
|
if (canaryDeploymentHelper.isSMICanaryStrategy()) {
|
||||||
const updatedManifests = appendStableVersionLabelToResource(files, kubectl);
|
const updatedManifests = appendStableVersionLabelToResource(files, kubectl);
|
||||||
result = kubectl.apply(updatedManifests, TaskInputParameters.forceDeployment);
|
result = kubectl.apply(updatedManifests, TaskInputParameters.forceDeployment);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = kubectl.apply(files, TaskInputParameters.forceDeployment);
|
result = kubectl.apply(files, TaskInputParameters.forceDeployment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
utility_1.checkForErrors([result]);
|
utility_1.checkForErrors([result]);
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
function appendStableVersionLabelToResource(files, kubectl) {
|
function appendStableVersionLabelToResource(files, kubectl) {
|
||||||
const manifestFiles = [];
|
const manifestFiles = [];
|
||||||
const newObjectsList = [];
|
const newObjectsList = [];
|
||||||
files.forEach((filePath) => {
|
files.forEach((filePath) => {
|
||||||
const fileContents = fs.readFileSync(filePath);
|
const fileContents = fs.readFileSync(filePath);
|
||||||
yaml.safeLoadAll(fileContents, function (inputObject) {
|
yaml.safeLoadAll(fileContents, function (inputObject) {
|
||||||
const kind = inputObject.kind;
|
const kind = inputObject.kind;
|
||||||
if (KubernetesObjectUtility.isDeploymentEntity(kind)) {
|
if (KubernetesObjectUtility.isDeploymentEntity(kind)) {
|
||||||
const updatedObject = canaryDeploymentHelper.markResourceAsStable(inputObject);
|
const updatedObject = canaryDeploymentHelper.markResourceAsStable(inputObject);
|
||||||
newObjectsList.push(updatedObject);
|
newObjectsList.push(updatedObject);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
manifestFiles.push(filePath);
|
manifestFiles.push(filePath);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const updatedManifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
|
const updatedManifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
|
||||||
manifestFiles.push(...updatedManifestFiles);
|
manifestFiles.push(...updatedManifestFiles);
|
||||||
return manifestFiles;
|
return manifestFiles;
|
||||||
}
|
}
|
||||||
function checkManifestStability(kubectl, resources) {
|
function checkManifestStability(kubectl, resources) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
yield KubernetesManifestUtility.checkManifestStability(kubectl, resources);
|
yield KubernetesManifestUtility.checkManifestStability(kubectl, resources);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function annotateResources(files, kubectl, resourceTypes, allPods) {
|
function annotateAndLabelResources(files, kubectl, resourceTypes, allPods) {
|
||||||
const annotateResults = [];
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
annotateResults.push(utility_1.annotateNamespace(kubectl, TaskInputParameters.namespace));
|
const workflowFilePath = yield utility_1.getWorkflowFilePath(TaskInputParameters.githubToken);
|
||||||
annotateResults.push(kubectl.annotateFiles(files, models.workflowAnnotations, true));
|
const deploymentConfig = yield utility_1.getDeploymentConfig();
|
||||||
resourceTypes.forEach(resource => {
|
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
|
||||||
if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) {
|
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, deploymentConfig);
|
||||||
utility_1.annotateChildPods(kubectl, resource.type, resource.name, allPods)
|
labelResources(files, kubectl, annotationKeyLabel);
|
||||||
.forEach(execResult => annotateResults.push(execResult));
|
});
|
||||||
}
|
}
|
||||||
});
|
function annotateResources(files, kubectl, resourceTypes, allPods, annotationKey, workflowFilePath, deploymentConfig) {
|
||||||
utility_1.checkForErrors(annotateResults, true);
|
const annotateResults = [];
|
||||||
}
|
const lastSuccessSha = utility_1.getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey);
|
||||||
function isCanaryDeploymentStrategy(deploymentStrategy) {
|
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath, deploymentConfig);
|
||||||
return deploymentStrategy != null && deploymentStrategy.toUpperCase() === canaryDeploymentHelper.CANARY_DEPLOYMENT_STRATEGY.toUpperCase();
|
annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, annotationKeyValStr));
|
||||||
}
|
annotateResults.push(kubectl.annotateFiles(files, annotationKeyValStr));
|
||||||
|
resourceTypes.forEach(resource => {
|
||||||
|
if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) {
|
||||||
|
utility_1.annotateChildPods(kubectl, resource.type, resource.name, annotationKeyValStr, allPods)
|
||||||
|
.forEach(execResult => annotateResults.push(execResult));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
utility_1.checkForErrors(annotateResults, true);
|
||||||
|
}
|
||||||
|
function labelResources(files, kubectl, label) {
|
||||||
|
const labels = [`workflowFriendlyName=${utility_1.normaliseWorkflowStrLabel(process.env.GITHUB_WORKFLOW)}`, `workflow=${label}`];
|
||||||
|
utility_1.checkForErrors([kubectl.labelFiles(files, labels)], true);
|
||||||
|
}
|
||||||
|
function isCanaryDeploymentStrategy(deploymentStrategy) {
|
||||||
|
return deploymentStrategy != null && deploymentStrategy.toUpperCase() === canaryDeploymentHelper.CANARY_DEPLOYMENT_STRATEGY.toUpperCase();
|
||||||
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ exports.rejectBlueGreenIngress = rejectBlueGreenIngress;
|
|||||||
function routeBlueGreenIngress(kubectl, nextLabel, serviceNameMap, ingressEntityList) {
|
function routeBlueGreenIngress(kubectl, nextLabel, serviceNameMap, ingressEntityList) {
|
||||||
let newObjectsList = [];
|
let newObjectsList = [];
|
||||||
if (!nextLabel) {
|
if (!nextLabel) {
|
||||||
newObjectsList = newObjectsList.concat(ingressEntityList);
|
newObjectsList = ingressEntityList.filter(ingress => isIngressRouted(ingress, serviceNameMap));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ingressEntityList.forEach((inputObject) => {
|
ingressEntityList.forEach((inputObject) => {
|
||||||
|
|||||||
@@ -1,58 +1,58 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.deployPodCanary = void 0;
|
exports.deployPodCanary = void 0;
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const yaml = require("js-yaml");
|
const yaml = require("js-yaml");
|
||||||
const TaskInputParameters = require("../../input-parameters");
|
const TaskInputParameters = require("../../input-parameters");
|
||||||
const fileHelper = require("../files-helper");
|
const fileHelper = require("../files-helper");
|
||||||
const helper = require("../resource-object-utility");
|
const helper = require("../resource-object-utility");
|
||||||
const canaryDeploymentHelper = require("./canary-deployment-helper");
|
const canaryDeploymentHelper = require("./canary-deployment-helper");
|
||||||
function deployPodCanary(kubectl, filePaths) {
|
function deployPodCanary(kubectl, filePaths) {
|
||||||
const newObjectsList = [];
|
const newObjectsList = [];
|
||||||
const percentage = parseInt(TaskInputParameters.canaryPercentage);
|
const percentage = parseInt(TaskInputParameters.canaryPercentage);
|
||||||
filePaths.forEach((filePath) => {
|
filePaths.forEach((filePath) => {
|
||||||
const fileContents = fs.readFileSync(filePath);
|
const fileContents = fs.readFileSync(filePath);
|
||||||
yaml.safeLoadAll(fileContents, function (inputObject) {
|
yaml.safeLoadAll(fileContents, function (inputObject) {
|
||||||
const name = inputObject.metadata.name;
|
const name = inputObject.metadata.name;
|
||||||
const kind = inputObject.kind;
|
const kind = inputObject.kind;
|
||||||
if (helper.isDeploymentEntity(kind)) {
|
if (helper.isDeploymentEntity(kind)) {
|
||||||
core.debug('Calculating replica count for canary');
|
core.debug('Calculating replica count for canary');
|
||||||
const canaryReplicaCount = calculateReplicaCountForCanary(inputObject, percentage);
|
const canaryReplicaCount = calculateReplicaCountForCanary(inputObject, percentage);
|
||||||
core.debug('Replica count is ' + canaryReplicaCount);
|
core.debug('Replica count is ' + canaryReplicaCount);
|
||||||
// Get stable object
|
// Get stable object
|
||||||
core.debug('Querying stable object');
|
core.debug('Querying stable object');
|
||||||
const stableObject = canaryDeploymentHelper.fetchResource(kubectl, kind, name);
|
const stableObject = canaryDeploymentHelper.fetchResource(kubectl, kind, name);
|
||||||
if (!stableObject) {
|
if (!stableObject) {
|
||||||
core.debug('Stable object not found. Creating only canary object');
|
core.debug('Stable object not found. Creating only canary object');
|
||||||
// If stable object not found, create canary deployment.
|
// If stable object not found, create canary deployment.
|
||||||
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(inputObject, canaryReplicaCount);
|
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(inputObject, canaryReplicaCount);
|
||||||
core.debug('New canary object is: ' + JSON.stringify(newCanaryObject));
|
core.debug('New canary object is: ' + JSON.stringify(newCanaryObject));
|
||||||
newObjectsList.push(newCanaryObject);
|
newObjectsList.push(newCanaryObject);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.debug('Stable object found. Creating canary and baseline objects');
|
core.debug('Stable object found. Creating canary and baseline objects');
|
||||||
// If canary object not found, create canary and baseline object.
|
// If canary object not found, create canary and baseline object.
|
||||||
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(inputObject, canaryReplicaCount);
|
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(inputObject, canaryReplicaCount);
|
||||||
const newBaselineObject = canaryDeploymentHelper.getNewBaselineResource(stableObject, canaryReplicaCount);
|
const newBaselineObject = canaryDeploymentHelper.getNewBaselineResource(stableObject, canaryReplicaCount);
|
||||||
core.debug('New canary object is: ' + JSON.stringify(newCanaryObject));
|
core.debug('New canary object is: ' + JSON.stringify(newCanaryObject));
|
||||||
core.debug('New baseline object is: ' + JSON.stringify(newBaselineObject));
|
core.debug('New baseline object is: ' + JSON.stringify(newBaselineObject));
|
||||||
newObjectsList.push(newCanaryObject);
|
newObjectsList.push(newCanaryObject);
|
||||||
newObjectsList.push(newBaselineObject);
|
newObjectsList.push(newBaselineObject);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Updating non deployment entity as it is.
|
// Updating non deployment entity as it is.
|
||||||
newObjectsList.push(inputObject);
|
newObjectsList.push(inputObject);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
|
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
|
||||||
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
|
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
|
||||||
return { 'result': result, 'newFilePaths': manifestFiles };
|
return { 'result': result, 'newFilePaths': manifestFiles };
|
||||||
}
|
}
|
||||||
exports.deployPodCanary = deployPodCanary;
|
exports.deployPodCanary = deployPodCanary;
|
||||||
function calculateReplicaCountForCanary(inputObject, percentage) {
|
function calculateReplicaCountForCanary(inputObject, percentage) {
|
||||||
const inputReplicaCount = helper.getReplicaCount(inputObject);
|
const inputReplicaCount = helper.getReplicaCount(inputObject);
|
||||||
return Math.round((inputReplicaCount * percentage) / 100);
|
return Math.round((inputReplicaCount * percentage) / 100);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,9 +174,9 @@ exports.validateTrafficSplitsState = validateTrafficSplitsState;
|
|||||||
function cleanupSMI(kubectl, serviceEntityList) {
|
function cleanupSMI(kubectl, serviceEntityList) {
|
||||||
const deleteList = [];
|
const deleteList = [];
|
||||||
serviceEntityList.forEach((serviceObject) => {
|
serviceEntityList.forEach((serviceObject) => {
|
||||||
|
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), kind: TRAFFIC_SPLIT_OBJECT });
|
||||||
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.GREEN_SUFFIX), kind: serviceObject.kind });
|
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.GREEN_SUFFIX), kind: serviceObject.kind });
|
||||||
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.STABLE_SUFFIX), kind: serviceObject.kind });
|
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, blue_green_helper_2.STABLE_SUFFIX), kind: serviceObject.kind });
|
||||||
deleteList.push({ name: blue_green_helper_1.getBlueGreenResourceName(serviceObject.metadata.name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), kind: TRAFFIC_SPLIT_OBJECT });
|
|
||||||
});
|
});
|
||||||
// deleting all objects
|
// deleting all objects
|
||||||
blue_green_helper_1.deleteObjects(kubectl, deleteList);
|
blue_green_helper_1.deleteObjects(kubectl, deleteList);
|
||||||
|
|||||||
@@ -1,199 +1,196 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.redirectTrafficToStableDeployment = exports.redirectTrafficToCanaryDeployment = exports.deploySMICanary = void 0;
|
exports.redirectTrafficToStableDeployment = exports.redirectTrafficToCanaryDeployment = exports.deploySMICanary = void 0;
|
||||||
const core = require("@actions/core");
|
const core = require("@actions/core");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const yaml = require("js-yaml");
|
const yaml = require("js-yaml");
|
||||||
const util = require("util");
|
const TaskInputParameters = require("../../input-parameters");
|
||||||
const TaskInputParameters = require("../../input-parameters");
|
const fileHelper = require("../files-helper");
|
||||||
const fileHelper = require("../files-helper");
|
const helper = require("../resource-object-utility");
|
||||||
const helper = require("../resource-object-utility");
|
const utils = require("../manifest-utilities");
|
||||||
const utils = require("../manifest-utilities");
|
const kubectlUtils = require("../kubectl-util");
|
||||||
const kubectlUtils = require("../kubectl-util");
|
const canaryDeploymentHelper = require("./canary-deployment-helper");
|
||||||
const canaryDeploymentHelper = require("./canary-deployment-helper");
|
const utility_1 = require("../utility");
|
||||||
const utility_1 = require("../utility");
|
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';
|
let trafficSplitAPIVersion = "";
|
||||||
let trafficSplitAPIVersion = "";
|
function deploySMICanary(kubectl, filePaths) {
|
||||||
function deploySMICanary(kubectl, filePaths) {
|
const newObjectsList = [];
|
||||||
const newObjectsList = [];
|
const canaryReplicaCount = parseInt(TaskInputParameters.baselineAndCanaryReplicas);
|
||||||
const canaryReplicaCount = parseInt(TaskInputParameters.baselineAndCanaryReplicas);
|
core.debug('Replica count is ' + canaryReplicaCount);
|
||||||
core.debug('Replica count is ' + canaryReplicaCount);
|
filePaths.forEach((filePath) => {
|
||||||
filePaths.forEach((filePath) => {
|
const fileContents = fs.readFileSync(filePath);
|
||||||
const fileContents = fs.readFileSync(filePath);
|
yaml.safeLoadAll(fileContents, function (inputObject) {
|
||||||
yaml.safeLoadAll(fileContents, function (inputObject) {
|
const name = inputObject.metadata.name;
|
||||||
const name = inputObject.metadata.name;
|
const kind = inputObject.kind;
|
||||||
const kind = inputObject.kind;
|
if (helper.isDeploymentEntity(kind)) {
|
||||||
if (helper.isDeploymentEntity(kind)) {
|
// Get stable object
|
||||||
// Get stable object
|
core.debug('Querying stable object');
|
||||||
core.debug('Querying stable object');
|
const stableObject = canaryDeploymentHelper.fetchResource(kubectl, kind, name);
|
||||||
const stableObject = canaryDeploymentHelper.fetchResource(kubectl, kind, name);
|
if (!stableObject) {
|
||||||
if (!stableObject) {
|
core.debug('Stable object not found. Creating only canary object');
|
||||||
core.debug('Stable object not found. Creating only canary object');
|
// If stable object not found, create canary deployment.
|
||||||
// If stable object not found, create canary deployment.
|
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(inputObject, canaryReplicaCount);
|
||||||
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(inputObject, canaryReplicaCount);
|
core.debug('New canary object is: ' + JSON.stringify(newCanaryObject));
|
||||||
core.debug('New canary object is: ' + JSON.stringify(newCanaryObject));
|
newObjectsList.push(newCanaryObject);
|
||||||
newObjectsList.push(newCanaryObject);
|
}
|
||||||
}
|
else {
|
||||||
else {
|
if (!canaryDeploymentHelper.isResourceMarkedAsStable(stableObject)) {
|
||||||
if (!canaryDeploymentHelper.isResourceMarkedAsStable(stableObject)) {
|
throw (`StableSpecSelectorNotExist : ${name}`);
|
||||||
throw (`StableSpecSelectorNotExist : ${name}`);
|
}
|
||||||
}
|
core.debug('Stable object found. Creating canary and baseline objects');
|
||||||
core.debug('Stable object found. Creating canary and baseline objects');
|
// If canary object not found, create canary and baseline object.
|
||||||
// If canary object not found, create canary and baseline object.
|
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(inputObject, canaryReplicaCount);
|
||||||
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(inputObject, canaryReplicaCount);
|
const newBaselineObject = canaryDeploymentHelper.getNewBaselineResource(stableObject, canaryReplicaCount);
|
||||||
const newBaselineObject = canaryDeploymentHelper.getNewBaselineResource(stableObject, canaryReplicaCount);
|
core.debug('New canary object is: ' + JSON.stringify(newCanaryObject));
|
||||||
core.debug('New canary object is: ' + JSON.stringify(newCanaryObject));
|
core.debug('New baseline object is: ' + JSON.stringify(newBaselineObject));
|
||||||
core.debug('New baseline object is: ' + JSON.stringify(newBaselineObject));
|
newObjectsList.push(newCanaryObject);
|
||||||
newObjectsList.push(newCanaryObject);
|
newObjectsList.push(newBaselineObject);
|
||||||
newObjectsList.push(newBaselineObject);
|
}
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
else {
|
// Updating non deployment entity as it is.
|
||||||
// Updating non deployment entity as it is.
|
newObjectsList.push(inputObject);
|
||||||
newObjectsList.push(inputObject);
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
});
|
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
|
||||||
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
|
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
|
||||||
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
|
createCanaryService(kubectl, filePaths);
|
||||||
createCanaryService(kubectl, filePaths);
|
return { 'result': result, 'newFilePaths': manifestFiles };
|
||||||
return { 'result': result, 'newFilePaths': manifestFiles };
|
}
|
||||||
}
|
exports.deploySMICanary = deploySMICanary;
|
||||||
exports.deploySMICanary = deploySMICanary;
|
function createCanaryService(kubectl, filePaths) {
|
||||||
function createCanaryService(kubectl, filePaths) {
|
const newObjectsList = [];
|
||||||
const newObjectsList = [];
|
const trafficObjectsList = [];
|
||||||
const trafficObjectsList = [];
|
filePaths.forEach((filePath) => {
|
||||||
filePaths.forEach((filePath) => {
|
const fileContents = fs.readFileSync(filePath);
|
||||||
const fileContents = fs.readFileSync(filePath);
|
yaml.safeLoadAll(fileContents, function (inputObject) {
|
||||||
yaml.safeLoadAll(fileContents, function (inputObject) {
|
const name = inputObject.metadata.name;
|
||||||
const name = inputObject.metadata.name;
|
const kind = inputObject.kind;
|
||||||
const kind = inputObject.kind;
|
if (helper.isServiceEntity(kind)) {
|
||||||
if (helper.isServiceEntity(kind)) {
|
const newCanaryServiceObject = canaryDeploymentHelper.getNewCanaryResource(inputObject);
|
||||||
const newCanaryServiceObject = canaryDeploymentHelper.getNewCanaryResource(inputObject);
|
core.debug('New canary service object is: ' + JSON.stringify(newCanaryServiceObject));
|
||||||
core.debug('New canary service object is: ' + JSON.stringify(newCanaryServiceObject));
|
newObjectsList.push(newCanaryServiceObject);
|
||||||
newObjectsList.push(newCanaryServiceObject);
|
const newBaselineServiceObject = canaryDeploymentHelper.getNewBaselineResource(inputObject);
|
||||||
const newBaselineServiceObject = canaryDeploymentHelper.getNewBaselineResource(inputObject);
|
core.debug('New baseline object is: ' + JSON.stringify(newBaselineServiceObject));
|
||||||
core.debug('New baseline object is: ' + JSON.stringify(newBaselineServiceObject));
|
newObjectsList.push(newBaselineServiceObject);
|
||||||
newObjectsList.push(newBaselineServiceObject);
|
core.debug('Querying for stable service object');
|
||||||
core.debug('Querying for stable service object');
|
const stableObject = canaryDeploymentHelper.fetchResource(kubectl, kind, canaryDeploymentHelper.getStableResourceName(name));
|
||||||
const stableObject = canaryDeploymentHelper.fetchResource(kubectl, kind, canaryDeploymentHelper.getStableResourceName(name));
|
if (!stableObject) {
|
||||||
if (!stableObject) {
|
const newStableServiceObject = canaryDeploymentHelper.getStableResource(inputObject);
|
||||||
const newStableServiceObject = canaryDeploymentHelper.getStableResource(inputObject);
|
core.debug('New stable service object is: ' + JSON.stringify(newStableServiceObject));
|
||||||
core.debug('New stable service object is: ' + JSON.stringify(newStableServiceObject));
|
newObjectsList.push(newStableServiceObject);
|
||||||
newObjectsList.push(newStableServiceObject);
|
core.debug('Creating the traffic object for service: ' + name);
|
||||||
core.debug('Creating the traffic object for service: ' + name);
|
const trafficObject = createTrafficSplitManifestFile(kubectl, name, 0, 0, 1000);
|
||||||
const trafficObject = createTrafficSplitManifestFile(kubectl, name, 0, 0, 1000);
|
core.debug('Creating the traffic object for service: ' + trafficObject);
|
||||||
core.debug('Creating the traffic object for service: ' + trafficObject);
|
trafficObjectsList.push(trafficObject);
|
||||||
trafficObjectsList.push(trafficObject);
|
}
|
||||||
}
|
else {
|
||||||
else {
|
let updateTrafficObject = true;
|
||||||
let updateTrafficObject = true;
|
const trafficObject = canaryDeploymentHelper.fetchResource(kubectl, TRAFFIC_SPLIT_OBJECT, getTrafficSplitResourceName(name));
|
||||||
const trafficObject = canaryDeploymentHelper.fetchResource(kubectl, TRAFFIC_SPLIT_OBJECT, getTrafficSplitResourceName(name));
|
if (trafficObject) {
|
||||||
if (trafficObject) {
|
const trafficJObject = JSON.parse(JSON.stringify(trafficObject));
|
||||||
const trafficJObject = JSON.parse(JSON.stringify(trafficObject));
|
if (trafficJObject && trafficJObject.spec && trafficJObject.spec.backends) {
|
||||||
if (trafficJObject && trafficJObject.spec && trafficJObject.spec.backends) {
|
trafficJObject.spec.backends.forEach((s) => {
|
||||||
trafficJObject.spec.backends.forEach((s) => {
|
if (s.service === canaryDeploymentHelper.getCanaryResourceName(name) && s.weight === "1000m") {
|
||||||
if (s.service === canaryDeploymentHelper.getCanaryResourceName(name) && s.weight === "1000m") {
|
core.debug('Update traffic objcet not required');
|
||||||
core.debug('Update traffic objcet not required');
|
updateTrafficObject = false;
|
||||||
updateTrafficObject = false;
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
}
|
if (updateTrafficObject) {
|
||||||
if (updateTrafficObject) {
|
core.debug('Stable service object present so updating the traffic object for service: ' + name);
|
||||||
core.debug('Stable service object present so updating the traffic object for service: ' + name);
|
trafficObjectsList.push(updateTrafficSplitObject(kubectl, name));
|
||||||
trafficObjectsList.push(updateTrafficSplitObject(kubectl, name));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
});
|
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
|
||||||
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList);
|
manifestFiles.push(...trafficObjectsList);
|
||||||
manifestFiles.push(...trafficObjectsList);
|
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
|
||||||
const result = kubectl.apply(manifestFiles, TaskInputParameters.forceDeployment);
|
utility_1.checkForErrors([result]);
|
||||||
utility_1.checkForErrors([result]);
|
}
|
||||||
}
|
function redirectTrafficToCanaryDeployment(kubectl, manifestFilePaths) {
|
||||||
function redirectTrafficToCanaryDeployment(kubectl, manifestFilePaths) {
|
adjustTraffic(kubectl, manifestFilePaths, 0, 1000);
|
||||||
adjustTraffic(kubectl, manifestFilePaths, 0, 1000);
|
}
|
||||||
}
|
exports.redirectTrafficToCanaryDeployment = redirectTrafficToCanaryDeployment;
|
||||||
exports.redirectTrafficToCanaryDeployment = redirectTrafficToCanaryDeployment;
|
function redirectTrafficToStableDeployment(kubectl, manifestFilePaths) {
|
||||||
function redirectTrafficToStableDeployment(kubectl, manifestFilePaths) {
|
adjustTraffic(kubectl, manifestFilePaths, 1000, 0);
|
||||||
adjustTraffic(kubectl, manifestFilePaths, 1000, 0);
|
}
|
||||||
}
|
exports.redirectTrafficToStableDeployment = redirectTrafficToStableDeployment;
|
||||||
exports.redirectTrafficToStableDeployment = redirectTrafficToStableDeployment;
|
function adjustTraffic(kubectl, manifestFilePaths, stableWeight, canaryWeight) {
|
||||||
function adjustTraffic(kubectl, manifestFilePaths, stableWeight, canaryWeight) {
|
// get manifest files
|
||||||
// get manifest files
|
const inputManifestFiles = utils.getManifestFiles(manifestFilePaths);
|
||||||
const inputManifestFiles = utils.getManifestFiles(manifestFilePaths);
|
if (inputManifestFiles == null || inputManifestFiles.length == 0) {
|
||||||
if (inputManifestFiles == null || inputManifestFiles.length == 0) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
const trafficSplitManifests = [];
|
||||||
const trafficSplitManifests = [];
|
const serviceObjects = [];
|
||||||
const serviceObjects = [];
|
inputManifestFiles.forEach((filePath) => {
|
||||||
inputManifestFiles.forEach((filePath) => {
|
const fileContents = fs.readFileSync(filePath);
|
||||||
const fileContents = fs.readFileSync(filePath);
|
yaml.safeLoadAll(fileContents, function (inputObject) {
|
||||||
yaml.safeLoadAll(fileContents, function (inputObject) {
|
const name = inputObject.metadata.name;
|
||||||
const name = inputObject.metadata.name;
|
const kind = inputObject.kind;
|
||||||
const kind = inputObject.kind;
|
if (helper.isServiceEntity(kind)) {
|
||||||
if (helper.isServiceEntity(kind)) {
|
trafficSplitManifests.push(createTrafficSplitManifestFile(kubectl, name, stableWeight, 0, canaryWeight));
|
||||||
trafficSplitManifests.push(createTrafficSplitManifestFile(kubectl, name, stableWeight, 0, canaryWeight));
|
serviceObjects.push(name);
|
||||||
serviceObjects.push(name);
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
});
|
if (trafficSplitManifests.length <= 0) {
|
||||||
if (trafficSplitManifests.length <= 0) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
const result = kubectl.apply(trafficSplitManifests, TaskInputParameters.forceDeployment);
|
||||||
const result = kubectl.apply(trafficSplitManifests, TaskInputParameters.forceDeployment);
|
core.debug('serviceObjects:' + serviceObjects.join(',') + ' result:' + result);
|
||||||
core.debug('serviceObjects:' + serviceObjects.join(',') + ' result:' + result);
|
utility_1.checkForErrors([result]);
|
||||||
utility_1.checkForErrors([result]);
|
}
|
||||||
}
|
function updateTrafficSplitObject(kubectl, serviceName) {
|
||||||
function updateTrafficSplitObject(kubectl, serviceName) {
|
const percentage = parseInt(TaskInputParameters.canaryPercentage) * 10;
|
||||||
const percentage = parseInt(TaskInputParameters.canaryPercentage) * 10;
|
const baselineAndCanaryWeight = percentage / 2;
|
||||||
const baselineAndCanaryWeight = percentage / 2;
|
const stableDeploymentWeight = 1000 - percentage;
|
||||||
const stableDeploymentWeight = 1000 - percentage;
|
core.debug('Creating the traffic object with canary weight: ' + baselineAndCanaryWeight + ',baseling weight: ' + baselineAndCanaryWeight + ',stable: ' + stableDeploymentWeight);
|
||||||
core.debug('Creating the traffic object with canary weight: ' + baselineAndCanaryWeight + ',baseling weight: ' + baselineAndCanaryWeight + ',stable: ' + stableDeploymentWeight);
|
return createTrafficSplitManifestFile(kubectl, serviceName, stableDeploymentWeight, baselineAndCanaryWeight, baselineAndCanaryWeight);
|
||||||
return createTrafficSplitManifestFile(kubectl, serviceName, stableDeploymentWeight, baselineAndCanaryWeight, baselineAndCanaryWeight);
|
}
|
||||||
}
|
function createTrafficSplitManifestFile(kubectl, serviceName, stableWeight, baselineWeight, canaryWeight) {
|
||||||
function createTrafficSplitManifestFile(kubectl, serviceName, stableWeight, baselineWeight, canaryWeight) {
|
const smiObjectString = getTrafficSplitObject(kubectl, serviceName, stableWeight, baselineWeight, canaryWeight);
|
||||||
const smiObjectString = getTrafficSplitObject(kubectl, serviceName, stableWeight, baselineWeight, canaryWeight);
|
const manifestFile = fileHelper.writeManifestToFile(smiObjectString, TRAFFIC_SPLIT_OBJECT, serviceName);
|
||||||
const manifestFile = fileHelper.writeManifestToFile(smiObjectString, TRAFFIC_SPLIT_OBJECT, serviceName);
|
if (!manifestFile) {
|
||||||
if (!manifestFile) {
|
throw new Error('UnableToCreateTrafficSplitManifestFile');
|
||||||
throw new Error('UnableToCreateTrafficSplitManifestFile');
|
}
|
||||||
}
|
return manifestFile;
|
||||||
return manifestFile;
|
}
|
||||||
}
|
function getTrafficSplitObject(kubectl, name, stableWeight, baselineWeight, canaryWeight) {
|
||||||
function getTrafficSplitObject(kubectl, name, stableWeight, baselineWeight, canaryWeight) {
|
if (!trafficSplitAPIVersion) {
|
||||||
if (!trafficSplitAPIVersion) {
|
trafficSplitAPIVersion = kubectlUtils.getTrafficSplitAPIVersion(kubectl);
|
||||||
trafficSplitAPIVersion = kubectlUtils.getTrafficSplitAPIVersion(kubectl);
|
}
|
||||||
}
|
return `{
|
||||||
const trafficSplitObjectJson = `{
|
|
||||||
"apiVersion": "${trafficSplitAPIVersion}",
|
"apiVersion": "${trafficSplitAPIVersion}",
|
||||||
"kind": "TrafficSplit",
|
"kind": "TrafficSplit",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "%s"
|
"name": "${getTrafficSplitResourceName(name)}"
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"backends": [
|
"backends": [
|
||||||
{
|
{
|
||||||
"service": "%s",
|
"service": "${canaryDeploymentHelper.getStableResourceName(name)}",
|
||||||
"weight": "%sm"
|
"weight": "${stableWeight}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"service": "%s",
|
"service": "${canaryDeploymentHelper.getBaselineResourceName(name)}",
|
||||||
"weight": "%sm"
|
"weight": "${baselineWeight}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"service": "%s",
|
"service": "${canaryDeploymentHelper.getCanaryResourceName(name)}",
|
||||||
"weight": "%sm"
|
"weight": "${canaryWeight}"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"service": "%s"
|
"service": "${name}"
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
const trafficSplitObject = util.format(trafficSplitObjectJson, getTrafficSplitResourceName(name), canaryDeploymentHelper.getStableResourceName(name), stableWeight, canaryDeploymentHelper.getBaselineResourceName(name), baselineWeight, canaryDeploymentHelper.getCanaryResourceName(name), canaryWeight, name);
|
}
|
||||||
return trafficSplitObject;
|
function getTrafficSplitResourceName(name) {
|
||||||
}
|
return name + TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX;
|
||||||
function getTrafficSplitResourceName(name) {
|
}
|
||||||
return name + TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.isEqual = exports.StringComparer = void 0;
|
exports.isEqual = exports.StringComparer = void 0;
|
||||||
var StringComparer;
|
var StringComparer;
|
||||||
(function (StringComparer) {
|
(function (StringComparer) {
|
||||||
StringComparer[StringComparer["Ordinal"] = 0] = "Ordinal";
|
StringComparer[StringComparer["Ordinal"] = 0] = "Ordinal";
|
||||||
StringComparer[StringComparer["OrdinalIgnoreCase"] = 1] = "OrdinalIgnoreCase";
|
StringComparer[StringComparer["OrdinalIgnoreCase"] = 1] = "OrdinalIgnoreCase";
|
||||||
})(StringComparer = exports.StringComparer || (exports.StringComparer = {}));
|
})(StringComparer = exports.StringComparer || (exports.StringComparer = {}));
|
||||||
function isEqual(str1, str2, stringComparer) {
|
function isEqual(str1, str2, stringComparer) {
|
||||||
if (str1 == null && str2 == null) {
|
if (str1 == null && str2 == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (str1 == null) {
|
if (str1 == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (str2 == null) {
|
if (str2 == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (stringComparer == StringComparer.OrdinalIgnoreCase) {
|
if (stringComparer == StringComparer.OrdinalIgnoreCase) {
|
||||||
return str1.toUpperCase() === str2.toUpperCase();
|
return str1.toUpperCase() === str2.toUpperCase();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return str1 === str2;
|
return str1 === str2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.isEqual = isEqual;
|
exports.isEqual = isEqual;
|
||||||
|
|||||||
+527
-527
File diff suppressed because it is too large
Load Diff
+228
-108
@@ -1,108 +1,228 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.annotateNamespace = exports.annotateChildPods = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0;
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
const os = require("os");
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
const core = require("@actions/core");
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
const constants_1 = require("../constants");
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
function getExecutableExtension() {
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
if (os.type().match(/^Win/)) {
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
return '.exe';
|
});
|
||||||
}
|
};
|
||||||
return '';
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
}
|
exports.getNormalizedPath = exports.isHttpUrl = exports.getCurrentTime = exports.getRandomInt = exports.sleep = exports.normaliseWorkflowStrLabel = exports.getDeploymentConfig = exports.annotateChildPods = exports.getWorkflowFilePath = exports.getLastSuccessfulRunSha = exports.checkForErrors = exports.isEqual = exports.getExecutableExtension = void 0;
|
||||||
exports.getExecutableExtension = getExecutableExtension;
|
const os = require("os");
|
||||||
function isEqual(str1, str2, ignoreCase) {
|
const core = require("@actions/core");
|
||||||
if (str1 == null && str2 == null) {
|
const githubClient_1 = require("../githubClient");
|
||||||
return true;
|
const httpClient_1 = require("./httpClient");
|
||||||
}
|
const inputParams = require("../input-parameters");
|
||||||
if (str1 == null || str2 == null) {
|
const docker_object_model_1 = require("../docker-object-model");
|
||||||
return false;
|
const io = require("@actions/io");
|
||||||
}
|
function getExecutableExtension() {
|
||||||
if (ignoreCase) {
|
if (os.type().match(/^Win/)) {
|
||||||
return str1.toUpperCase() === str2.toUpperCase();
|
return '.exe';
|
||||||
}
|
}
|
||||||
else {
|
return '';
|
||||||
return str1 === str2;
|
}
|
||||||
}
|
exports.getExecutableExtension = getExecutableExtension;
|
||||||
}
|
function isEqual(str1, str2, ignoreCase) {
|
||||||
exports.isEqual = isEqual;
|
if (str1 == null && str2 == null) {
|
||||||
function checkForErrors(execResults, warnIfError) {
|
return true;
|
||||||
if (execResults.length !== 0) {
|
}
|
||||||
let stderr = '';
|
if (str1 == null || str2 == null) {
|
||||||
execResults.forEach(result => {
|
return false;
|
||||||
if (result && result.stderr) {
|
}
|
||||||
if (result.code !== 0) {
|
if (ignoreCase) {
|
||||||
stderr += result.stderr + '\n';
|
return str1.toUpperCase() === str2.toUpperCase();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
core.warning(result.stderr);
|
return str1 === str2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
exports.isEqual = isEqual;
|
||||||
if (stderr.length > 0) {
|
function checkForErrors(execResults, warnIfError) {
|
||||||
if (warnIfError) {
|
if (execResults.length !== 0) {
|
||||||
core.warning(stderr.trim());
|
let stderr = '';
|
||||||
}
|
execResults.forEach(result => {
|
||||||
else {
|
if (result && result.stderr) {
|
||||||
throw new Error(stderr.trim());
|
if (result.code !== 0) {
|
||||||
}
|
stderr += result.stderr + '\n';
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
}
|
core.warning(result.stderr);
|
||||||
exports.checkForErrors = checkForErrors;
|
}
|
||||||
function annotateChildPods(kubectl, resourceType, resourceName, allPods) {
|
}
|
||||||
const commandExecutionResults = [];
|
});
|
||||||
let owner = resourceName;
|
if (stderr.length > 0) {
|
||||||
if (resourceType.toLowerCase().indexOf('deployment') > -1) {
|
if (warnIfError) {
|
||||||
owner = kubectl.getNewReplicaSet(resourceName);
|
core.warning(stderr.trim());
|
||||||
}
|
}
|
||||||
if (allPods && allPods.items && allPods.items.length > 0) {
|
else {
|
||||||
allPods.items.forEach((pod) => {
|
throw new Error(stderr.trim());
|
||||||
const owners = pod.metadata.ownerReferences;
|
}
|
||||||
if (owners) {
|
}
|
||||||
for (let ownerRef of owners) {
|
}
|
||||||
if (ownerRef.name === owner) {
|
}
|
||||||
commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, constants_1.workflowAnnotations, true));
|
exports.checkForErrors = checkForErrors;
|
||||||
break;
|
function getLastSuccessfulRunSha(kubectl, namespaceName, annotationKey) {
|
||||||
}
|
try {
|
||||||
}
|
const result = kubectl.getResource('namespace', namespaceName);
|
||||||
}
|
if (result) {
|
||||||
});
|
if (result.stderr) {
|
||||||
}
|
core.warning(`${result.stderr}`);
|
||||||
return commandExecutionResults;
|
return process.env.GITHUB_SHA;
|
||||||
}
|
}
|
||||||
exports.annotateChildPods = annotateChildPods;
|
else if (result.stdout) {
|
||||||
function annotateNamespace(kubectl, namespaceName) {
|
const annotationsSet = JSON.parse(result.stdout).metadata.annotations;
|
||||||
const result = kubectl.getResource('namespace', namespaceName);
|
if (annotationsSet && annotationsSet[annotationKey]) {
|
||||||
if (!result) {
|
return JSON.parse(annotationsSet[annotationKey].replace(/'/g, '"')).commit;
|
||||||
return { code: 1, stderr: 'Failed to get resource' };
|
}
|
||||||
}
|
else {
|
||||||
else {
|
return 'NA';
|
||||||
if (result.stderr) {
|
}
|
||||||
return result;
|
}
|
||||||
}
|
}
|
||||||
else if (result.stdout) {
|
}
|
||||||
const annotationsSet = JSON.parse(result.stdout).metadata.annotations;
|
catch (ex) {
|
||||||
if (annotationsSet && annotationsSet.runUri) {
|
core.warning(`Failed to get commits from cluster. ${JSON.stringify(ex)}`);
|
||||||
if (annotationsSet.runUri.indexOf(process.env['GITHUB_REPOSITORY']) == -1) {
|
return '';
|
||||||
core.debug(`Skipping 'annotate namespace' as namespace annotated by other workflow`);
|
}
|
||||||
return { code: 0, stdout: '' };
|
}
|
||||||
}
|
exports.getLastSuccessfulRunSha = getLastSuccessfulRunSha;
|
||||||
}
|
function getWorkflowFilePath(githubToken) {
|
||||||
return kubectl.annotate('namespace', namespaceName, constants_1.workflowAnnotations, true);
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
}
|
let workflowFilePath = process.env.GITHUB_WORKFLOW;
|
||||||
}
|
if (!workflowFilePath.startsWith('.github/workflows/')) {
|
||||||
}
|
const githubClient = new githubClient_1.GitHubClient(process.env.GITHUB_REPOSITORY, githubToken);
|
||||||
exports.annotateNamespace = annotateNamespace;
|
const response = yield githubClient.getWorkflows();
|
||||||
function sleep(timeout) {
|
if (response) {
|
||||||
return new Promise(resolve => setTimeout(resolve, timeout));
|
if (response.statusCode == httpClient_1.StatusCodes.OK
|
||||||
}
|
&& response.body
|
||||||
exports.sleep = sleep;
|
&& response.body.total_count) {
|
||||||
function getRandomInt(max) {
|
if (response.body.total_count > 0) {
|
||||||
return Math.floor(Math.random() * Math.floor(max));
|
for (let workflow of response.body.workflows) {
|
||||||
}
|
if (process.env.GITHUB_WORKFLOW === workflow.name) {
|
||||||
exports.getRandomInt = getRandomInt;
|
workflowFilePath = workflow.path;
|
||||||
function getCurrentTime() {
|
break;
|
||||||
return new Date().getTime();
|
}
|
||||||
}
|
}
|
||||||
exports.getCurrentTime = getCurrentTime;
|
}
|
||||||
|
}
|
||||||
|
else if (response.statusCode != httpClient_1.StatusCodes.OK) {
|
||||||
|
core.debug(`An error occured while getting list of workflows on the repo. Statuscode: ${response.statusCode}, StatusMessage: ${response.statusMessage}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.warning(`Failed to get response from workflow list API`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve(workflowFilePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.getWorkflowFilePath = getWorkflowFilePath;
|
||||||
|
function annotateChildPods(kubectl, resourceType, resourceName, annotationKeyValStr, allPods) {
|
||||||
|
const commandExecutionResults = [];
|
||||||
|
let owner = resourceName;
|
||||||
|
if (resourceType.toLowerCase().indexOf('deployment') > -1) {
|
||||||
|
owner = kubectl.getNewReplicaSet(resourceName);
|
||||||
|
}
|
||||||
|
if (allPods && allPods.items && allPods.items.length > 0) {
|
||||||
|
allPods.items.forEach((pod) => {
|
||||||
|
const owners = pod.metadata.ownerReferences;
|
||||||
|
if (owners) {
|
||||||
|
for (let ownerRef of owners) {
|
||||||
|
if (ownerRef.name === owner) {
|
||||||
|
commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, annotationKeyValStr));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return commandExecutionResults;
|
||||||
|
}
|
||||||
|
exports.annotateChildPods = annotateChildPods;
|
||||||
|
function getDeploymentConfig() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let helmChartPaths = (process.env.HELM_CHART_PATHS && process.env.HELM_CHART_PATHS.split(';').filter(path => path != "")) || [];
|
||||||
|
helmChartPaths = helmChartPaths.map(helmchart => getNormalizedPath(helmchart.trim()));
|
||||||
|
let inputManifestFiles = inputParams.manifests || [];
|
||||||
|
if (!helmChartPaths || helmChartPaths.length == 0) {
|
||||||
|
inputManifestFiles = inputManifestFiles.map(manifestFile => getNormalizedPath(manifestFile));
|
||||||
|
}
|
||||||
|
const imageNames = inputParams.containers || [];
|
||||||
|
let imageDockerfilePathMap = {};
|
||||||
|
//Fetching from image label if available
|
||||||
|
for (const image of imageNames) {
|
||||||
|
try {
|
||||||
|
imageDockerfilePathMap[image] = yield getDockerfilePath(image);
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
core.warning(`Failed to get dockerfile path for image ${image.toString()} | ` + ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const deploymentConfig = {
|
||||||
|
manifestFilePaths: inputManifestFiles,
|
||||||
|
helmChartFilePaths: helmChartPaths,
|
||||||
|
dockerfilePaths: imageDockerfilePathMap
|
||||||
|
};
|
||||||
|
return Promise.resolve(deploymentConfig);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.getDeploymentConfig = getDeploymentConfig;
|
||||||
|
function normaliseWorkflowStrLabel(workflowName) {
|
||||||
|
workflowName = workflowName.startsWith('.github/workflows/') ?
|
||||||
|
workflowName.replace(".github/workflows/", "") : workflowName;
|
||||||
|
return workflowName.replace(/ /g, "_");
|
||||||
|
}
|
||||||
|
exports.normaliseWorkflowStrLabel = normaliseWorkflowStrLabel;
|
||||||
|
function sleep(timeout) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, timeout));
|
||||||
|
}
|
||||||
|
exports.sleep = sleep;
|
||||||
|
function getRandomInt(max) {
|
||||||
|
return Math.floor(Math.random() * Math.floor(max));
|
||||||
|
}
|
||||||
|
exports.getRandomInt = getRandomInt;
|
||||||
|
function getCurrentTime() {
|
||||||
|
return new Date().getTime();
|
||||||
|
}
|
||||||
|
exports.getCurrentTime = getCurrentTime;
|
||||||
|
function checkDockerPath() {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let dockerPath = yield io.which('docker', false);
|
||||||
|
if (!dockerPath) {
|
||||||
|
throw new Error('Docker is not installed.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getDockerfilePath(image) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let imageConfig, imageInspectResult;
|
||||||
|
var dockerExec = new docker_object_model_1.DockerExec('docker');
|
||||||
|
yield checkDockerPath();
|
||||||
|
dockerExec.pull(image, [], true);
|
||||||
|
imageInspectResult = dockerExec.inspect(image, [], true);
|
||||||
|
imageConfig = JSON.parse(imageInspectResult)[0];
|
||||||
|
const DOCKERFILE_PATH_LABEL_KEY = 'dockerfile-path';
|
||||||
|
let pathValue = '';
|
||||||
|
if (imageConfig) {
|
||||||
|
if ((imageConfig.Config) && (imageConfig.Config.Labels) && (imageConfig.Config.Labels[DOCKERFILE_PATH_LABEL_KEY])) {
|
||||||
|
const pathLabel = imageConfig.Config.Labels[DOCKERFILE_PATH_LABEL_KEY];
|
||||||
|
pathValue = getNormalizedPath(pathLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve(pathValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function isHttpUrl(url) {
|
||||||
|
const HTTP_REGEX = /^https?:\/\/.*$/;
|
||||||
|
return HTTP_REGEX.test(url);
|
||||||
|
}
|
||||||
|
exports.isHttpUrl = isHttpUrl;
|
||||||
|
function getNormalizedPath(pathValue) {
|
||||||
|
if (!isHttpUrl(pathValue)) { //if it is not an http url then convert to link from current repo and commit
|
||||||
|
return `https://github.com/${process.env.GITHUB_REPOSITORY}/blob/${process.env.GITHUB_SHA}/${pathValue}`;
|
||||||
|
}
|
||||||
|
return pathValue;
|
||||||
|
}
|
||||||
|
exports.getNormalizedPath = getNormalizedPath;
|
||||||
|
|||||||
Generated
+10611
-98
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -8,16 +8,16 @@
|
|||||||
"test": "jest"
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/tool-cache": "^1.0.0",
|
"@actions/tool-cache": "1.1.2",
|
||||||
"@actions/io": "^1.0.0",
|
"@actions/io": "^1.0.0",
|
||||||
"@actions/core": "^1.0.0",
|
"@actions/core": "^1.2.6",
|
||||||
"@actions/exec": "^1.0.0",
|
"@actions/exec": "^1.0.0",
|
||||||
"js-yaml": "3.13.1"
|
"js-yaml": "3.13.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^12.0.10",
|
"@types/node": "^12.0.10",
|
||||||
"jest": "^25.0.0",
|
"jest": "^26.0.0",
|
||||||
"@types/jest": "^25.2.2",
|
"@types/jest": "^26.0.0",
|
||||||
"ts-jest": "^25.5.1",
|
"ts-jest": "^25.5.1",
|
||||||
"typescript": "3.9.5"
|
"typescript": "3.9.5"
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-12
@@ -1,4 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
import { DeploymentConfig } from "./utilities/utility";
|
||||||
|
|
||||||
export class KubernetesWorkload {
|
export class KubernetesWorkload {
|
||||||
public static pod: string = 'Pod';
|
public static pod: string = 'Pod';
|
||||||
@@ -25,15 +26,30 @@ export const deploymentTypes: string[] = ['deployment', 'replicaset', 'daemonset
|
|||||||
export const workloadTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
|
export const workloadTypes: string[] = ['deployment', 'replicaset', 'daemonset', 'pod', 'statefulset', 'job', 'cronjob'];
|
||||||
export const workloadTypesWithRolloutStatus: string[] = ['deployment', 'daemonset', 'statefulset'];
|
export const workloadTypesWithRolloutStatus: string[] = ['deployment', 'daemonset', 'statefulset'];
|
||||||
|
|
||||||
export const workflowAnnotations = [
|
export function getWorkflowAnnotationsJson(lastSuccessRunSha: string, workflowFilePath: string, deploymentConfig: DeploymentConfig): string {
|
||||||
`run=${process.env['GITHUB_RUN_ID']}`,
|
let annotationObject: any = {};
|
||||||
`repository=${process.env['GITHUB_REPOSITORY']}`,
|
annotationObject["run"] = process.env.GITHUB_RUN_ID;
|
||||||
`workflow=${process.env['GITHUB_WORKFLOW']}`,
|
annotationObject["repository"] = process.env.GITHUB_REPOSITORY;
|
||||||
`jobName=${process.env['GITHUB_JOB']}`,
|
annotationObject["workflow"] = process.env.GITHUB_WORKFLOW;
|
||||||
`createdBy=${process.env['GITHUB_ACTOR']}`,
|
annotationObject["workflowFileName"] = workflowFilePath.replace(".github/workflows/", "");
|
||||||
`runUri=https://github.com/${process.env['GITHUB_REPOSITORY']}/actions/runs/${process.env['GITHUB_RUN_ID']}`,
|
annotationObject["jobName"] = process.env.GITHUB_JOB;
|
||||||
`commit=${process.env['GITHUB_SHA']}`,
|
annotationObject["createdBy"] = process.env.GITHUB_ACTOR;
|
||||||
`branch=${process.env['GITHUB_REF']}`,
|
annotationObject["runUri"] = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
|
||||||
`deployTimestamp=${Date.now()}`,
|
annotationObject["commit"] = process.env.GITHUB_SHA;
|
||||||
`provider=GitHub`
|
annotationObject["lastSuccessRunCommit"] = lastSuccessRunSha;
|
||||||
];
|
annotationObject["branch"] = process.env.GITHUB_REF;
|
||||||
|
annotationObject["deployTimestamp"] = Date.now();
|
||||||
|
annotationObject["dockerfilePaths"] = deploymentConfig.dockerfilePaths;
|
||||||
|
annotationObject["manifestsPaths"] = deploymentConfig.manifestFilePaths
|
||||||
|
annotationObject["helmChartPaths"] = deploymentConfig.helmChartFilePaths;
|
||||||
|
annotationObject["provider"] = "GitHub";
|
||||||
|
|
||||||
|
return JSON.stringify(annotationObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getWorkflowAnnotationKeyLabel(workflowFilePath: string): string {
|
||||||
|
const hashKey = require("crypto").createHash("MD5")
|
||||||
|
.update(`${process.env.GITHUB_REPOSITORY}/${workflowFilePath}`)
|
||||||
|
.digest("hex");
|
||||||
|
return `githubWorkflow_${hashKey}`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { ToolRunner, IExecOptions, IExecSyncResult } from "./utilities/tool-runner";
|
||||||
|
|
||||||
|
export class DockerExec {
|
||||||
|
private dockerPath: string;
|
||||||
|
|
||||||
|
constructor(dockerPath: string) {
|
||||||
|
this.dockerPath = dockerPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
public pull(image: string, args: string[], silent?: boolean) {
|
||||||
|
args = ['pull', image, ...args];
|
||||||
|
let result: IExecSyncResult = this.execute(args, silent);
|
||||||
|
if (result.stderr != '' && result.code != 0) {
|
||||||
|
throw new Error(`docker images pull failed with: ${result.error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public inspect(image: string, args: string[], silent?: boolean): any {
|
||||||
|
args = ['inspect', image, ...args];
|
||||||
|
let result: IExecSyncResult = this.execute(args, silent);
|
||||||
|
if (result.stderr != '' && result.code != 0) {
|
||||||
|
throw new Error(`docker inspect call failed with: ${result.error}`);
|
||||||
|
}
|
||||||
|
return result.stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private execute(args: string[], silent?: boolean) {
|
||||||
|
const command = new ToolRunner(this.dockerPath);
|
||||||
|
command.arg(args);
|
||||||
|
|
||||||
|
return command.execSync({ silent: !!silent } as IExecOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import * as core from '@actions/core';
|
||||||
|
import { WebRequest, WebResponse, sendRequest } from "./utilities/httpClient";
|
||||||
|
|
||||||
|
export class GitHubClient {
|
||||||
|
constructor(repository: string, token: string) {
|
||||||
|
this._repository = repository;
|
||||||
|
this._token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getWorkflows(): Promise<any> {
|
||||||
|
const getWorkflowFileNameUrl = `https://api.github.com/repos/${this._repository}/actions/workflows`;
|
||||||
|
const webRequest = new WebRequest();
|
||||||
|
webRequest.method = "GET";
|
||||||
|
webRequest.uri = getWorkflowFileNameUrl;
|
||||||
|
webRequest.headers = {
|
||||||
|
Authorization: `Bearer ${this._token}`
|
||||||
|
};
|
||||||
|
|
||||||
|
core.debug(`Getting workflows for repo: ${this._repository}`);
|
||||||
|
const response: WebResponse = await sendRequest(webRequest);
|
||||||
|
return Promise.resolve(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _repository: string;
|
||||||
|
private _token: string;
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import * as core from '@actions/core';
|
|||||||
export let namespace: string = core.getInput('namespace');
|
export let namespace: string = core.getInput('namespace');
|
||||||
export const containers: string[] = core.getInput('images').split('\n');
|
export const containers: string[] = core.getInput('images').split('\n');
|
||||||
export const imagePullSecrets: string[] = core.getInput('imagepullsecrets').split('\n').filter(secret => secret.trim().length > 0);
|
export const imagePullSecrets: string[] = core.getInput('imagepullsecrets').split('\n').filter(secret => secret.trim().length > 0);
|
||||||
export const manifests = core.getInput('manifests').split('\n');
|
export const manifests = core.getInput('manifests').split(/[\n,;]+/).filter(manifest => manifest.trim().length > 0);
|
||||||
export const canaryPercentage: string = core.getInput('percentage');
|
export const canaryPercentage: string = core.getInput('percentage');
|
||||||
export const deploymentStrategy: string = core.getInput('strategy');
|
export const deploymentStrategy: string = core.getInput('strategy');
|
||||||
export const trafficSplitMethod: string = core.getInput('traffic-split-method');
|
export const trafficSplitMethod: string = core.getInput('traffic-split-method');
|
||||||
@@ -14,12 +14,17 @@ export const versionSwitchBuffer: string = core.getInput('version-switch-buffer'
|
|||||||
export const baselineAndCanaryReplicas: string = core.getInput('baseline-and-canary-replicas');
|
export const baselineAndCanaryReplicas: string = core.getInput('baseline-and-canary-replicas');
|
||||||
export const args: string = core.getInput('arguments');
|
export const args: string = core.getInput('arguments');
|
||||||
export const forceDeployment: boolean = core.getInput('force').toLowerCase() == 'true';
|
export const forceDeployment: boolean = core.getInput('force').toLowerCase() == 'true';
|
||||||
|
export const githubToken = core.getInput("token");
|
||||||
|
|
||||||
if (!namespace) {
|
if (!namespace) {
|
||||||
core.debug('Namespace was not supplied; using "default" namespace instead.');
|
core.debug('Namespace was not supplied; using "default" namespace instead.');
|
||||||
namespace = 'default';
|
namespace = 'default';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!githubToken) {
|
||||||
|
core.error("'token' input is not supplied. Set it to a PAT/GITHUB_TOKEN");
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pe = parseInt(canaryPercentage);
|
const pe = parseInt(canaryPercentage);
|
||||||
if (pe < 0 || pe > 100) {
|
if (pe < 0 || pe > 100) {
|
||||||
|
|||||||
@@ -50,18 +50,26 @@ export class Kubectl {
|
|||||||
return newReplicaSet;
|
return newReplicaSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public annotate(resourceType: string, resourceName: string, annotations: string[], overwrite?: boolean): IExecSyncResult {
|
public annotate(resourceType: string, resourceName: string, annotation: string): IExecSyncResult {
|
||||||
let args = ['annotate', resourceType, resourceName];
|
let args = ['annotate', resourceType, resourceName];
|
||||||
args = args.concat(annotations);
|
args.push(annotation);
|
||||||
if (!!overwrite) { args.push(`--overwrite`); }
|
args.push(`--overwrite`);
|
||||||
return this.execute(args);
|
return this.execute(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public annotateFiles(files: string | string[], annotations: string[], overwrite?: boolean): IExecSyncResult {
|
public annotateFiles(files: string | string[], annotation: string): IExecSyncResult {
|
||||||
let args = ['annotate'];
|
let args = ['annotate'];
|
||||||
args = args.concat(['-f', this.createInlineArray(files)]);
|
args = args.concat(['-f', this.createInlineArray(files)]);
|
||||||
args = args.concat(annotations);
|
args.push(annotation);
|
||||||
if (!!overwrite) { args.push(`--overwrite`); }
|
args.push(`--overwrite`);
|
||||||
|
return this.execute(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public labelFiles(files: string | string[], labels: string[]): IExecSyncResult {
|
||||||
|
let args = ['label'];
|
||||||
|
args = args.concat(['-f', this.createInlineArray(files)]);
|
||||||
|
args = args.concat(labels);
|
||||||
|
args.push(`--overwrite`);
|
||||||
return this.execute(args);
|
return this.execute(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -42,7 +42,7 @@ async function installKubectl(version: string) {
|
|||||||
|
|
||||||
function checkClusterContext() {
|
function checkClusterContext() {
|
||||||
if (!process.env["KUBECONFIG"]) {
|
if (!process.env["KUBECONFIG"]) {
|
||||||
throw new Error('Cluster context not set. Use k8ssetcontext action to set cluster context');
|
core.warning('KUBECONFIG env is not explicitly set. Ensure cluster context is set by using k8s-set-context / aks-set-context action.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +59,13 @@ export async function run() {
|
|||||||
namespace = 'default';
|
namespace = 'default';
|
||||||
}
|
}
|
||||||
let action = core.getInput('action');
|
let action = core.getInput('action');
|
||||||
let manifests = manifestsInput.split('\n');
|
let manifests = manifestsInput.split(/[\n,;]+/).filter(manifest => manifest.trim().length > 0);
|
||||||
|
|
||||||
|
if (manifests.length > 0) {
|
||||||
|
manifests = manifests.map(manifest => {
|
||||||
|
return manifest.trim();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (action === 'deploy') {
|
if (action === 'deploy') {
|
||||||
let strategy = core.getInput('strategy');
|
let strategy = core.getInput('strategy');
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// Taken from https://github.com/Azure/aks-set-context/blob/master/src/client.ts
|
||||||
|
import util = require("util");
|
||||||
|
import fs = require('fs');
|
||||||
|
import httpClient = require("typed-rest-client/HttpClient");
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
|
||||||
|
var httpCallbackClient = new httpClient.HttpClient('GITHUB_RUNNER', null, {});
|
||||||
|
|
||||||
|
export enum StatusCodes {
|
||||||
|
OK = 200,
|
||||||
|
CREATED = 201,
|
||||||
|
ACCEPTED = 202,
|
||||||
|
UNAUTHORIZED = 401,
|
||||||
|
NOT_FOUND = 404,
|
||||||
|
INTERNAL_SERVER_ERROR = 500,
|
||||||
|
SERVICE_UNAVAILABLE = 503
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebRequest {
|
||||||
|
public method: string;
|
||||||
|
public uri: string;
|
||||||
|
// body can be string or ReadableStream
|
||||||
|
public body: string | NodeJS.ReadableStream;
|
||||||
|
public headers: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebResponse {
|
||||||
|
public statusCode: number;
|
||||||
|
public statusMessage: string;
|
||||||
|
public headers: any;
|
||||||
|
public body: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WebRequestOptions {
|
||||||
|
public retriableErrorCodes?: string[];
|
||||||
|
public retryCount?: number;
|
||||||
|
public retryIntervalInSeconds?: number;
|
||||||
|
public retriableStatusCodes?: number[];
|
||||||
|
public retryRequestTimedout?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendRequest(request: WebRequest, options?: WebRequestOptions): Promise<WebResponse> {
|
||||||
|
let i = 0;
|
||||||
|
let retryCount = options && options.retryCount ? options.retryCount : 5;
|
||||||
|
let retryIntervalInSeconds = options && options.retryIntervalInSeconds ? options.retryIntervalInSeconds : 2;
|
||||||
|
let retriableErrorCodes = options && options.retriableErrorCodes ? options.retriableErrorCodes : ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN"];
|
||||||
|
let retriableStatusCodes = options && options.retriableStatusCodes ? options.retriableStatusCodes : [408, 409, 500, 502, 503, 504];
|
||||||
|
let timeToWait: number = retryIntervalInSeconds;
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
if (request.body && typeof (request.body) !== 'string' && !request.body["readable"]) {
|
||||||
|
request.body = fs.createReadStream(request.body["path"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
let response: WebResponse = await sendRequestInternal(request);
|
||||||
|
if (retriableStatusCodes.indexOf(response.statusCode) != -1 && ++i < retryCount) {
|
||||||
|
core.debug(util.format("Encountered a retriable status code: %s. Message: '%s'.", response.statusCode, response.statusMessage));
|
||||||
|
await sleepFor(timeToWait);
|
||||||
|
timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (retriableErrorCodes.indexOf(error.code) != -1 && ++i < retryCount) {
|
||||||
|
core.debug(util.format("Encountered a retriable error:%s. Message: %s.", error.code, error.message));
|
||||||
|
await sleepFor(timeToWait);
|
||||||
|
timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (error.code) {
|
||||||
|
core.debug("error code =" + error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sleepFor(sleepDurationInSeconds: number): Promise<any> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
setTimeout(resolve, sleepDurationInSeconds * 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendRequestInternal(request: WebRequest): Promise<WebResponse> {
|
||||||
|
core.debug(util.format("[%s]%s", request.method, request.uri));
|
||||||
|
var response: httpClient.HttpClientResponse = await httpCallbackClient.request(request.method, request.uri, request.body, request.headers);
|
||||||
|
return await toWebResponse(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toWebResponse(response: httpClient.HttpClientResponse): Promise<WebResponse> {
|
||||||
|
var res = new WebResponse();
|
||||||
|
if (response) {
|
||||||
|
res.statusCode = response.message.statusCode;
|
||||||
|
res.statusMessage = response.message.statusMessage;
|
||||||
|
res.headers = response.message.headers;
|
||||||
|
var body = await response.readBody();
|
||||||
|
if (body) {
|
||||||
|
try {
|
||||||
|
res.body = JSON.parse(body);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
core.debug("Could not parse response: " + JSON.stringify(error));
|
||||||
|
core.debug("Response: " + JSON.stringify(res.body));
|
||||||
|
res.body = body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ import * as toolCache from '@actions/tool-cache';
|
|||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
|
|
||||||
import { Kubectl } from '../kubectl-object-model';
|
import { Kubectl } from '../kubectl-object-model';
|
||||||
|
import { StatusCodes } from "./httpClient"
|
||||||
|
|
||||||
const kubectlToolName = 'kubectl';
|
const kubectlToolName = 'kubectl';
|
||||||
const stableKubectlVersion = 'v1.15.0';
|
const stableKubectlVersion = 'v1.15.0';
|
||||||
@@ -19,17 +20,25 @@ function getExecutableExtension(): string {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getkubectlDownloadURL(version: string): string {
|
function getKubectlArch(): string {
|
||||||
|
let arch = os.arch();
|
||||||
|
if (arch === 'x64') {
|
||||||
|
return 'amd64';
|
||||||
|
}
|
||||||
|
return arch;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getkubectlDownloadURL(version: string, arch: string): string {
|
||||||
switch (os.type()) {
|
switch (os.type()) {
|
||||||
case 'Linux':
|
case 'Linux':
|
||||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/amd64/kubectl', version);
|
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/linux/%s/kubectl', version, arch);
|
||||||
|
|
||||||
case 'Darwin':
|
case 'Darwin':
|
||||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/amd64/kubectl', version);
|
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/darwin/%s/kubectl', version, arch);
|
||||||
|
|
||||||
case 'Windows_NT':
|
case 'Windows_NT':
|
||||||
default:
|
default:
|
||||||
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/amd64/kubectl.exe', version);
|
return util.format('https://storage.googleapis.com/kubernetes-release/release/%s/bin/windows/%s/kubectl.exe', version, arch);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,11 +60,16 @@ export async function getStableKubectlVersion(): Promise<string> {
|
|||||||
export async function downloadKubectl(version: string): Promise<string> {
|
export async function downloadKubectl(version: string): Promise<string> {
|
||||||
let cachedToolpath = toolCache.find(kubectlToolName, version);
|
let cachedToolpath = toolCache.find(kubectlToolName, version);
|
||||||
let kubectlDownloadPath = '';
|
let kubectlDownloadPath = '';
|
||||||
|
let arch = getKubectlArch();
|
||||||
if (!cachedToolpath) {
|
if (!cachedToolpath) {
|
||||||
try {
|
try {
|
||||||
kubectlDownloadPath = await toolCache.downloadTool(getkubectlDownloadURL(version));
|
kubectlDownloadPath = await toolCache.downloadTool(getkubectlDownloadURL(version, arch));
|
||||||
} catch (exception) {
|
} catch (exception) {
|
||||||
throw new Error('DownloadKubectlFailed');
|
if (exception instanceof toolCache.HTTPError && exception.httpStatusCode === StatusCodes.NOT_FOUND) {
|
||||||
|
throw new Error(util.format("Kubectl '%s' for '%s' arch not found.", version, arch));
|
||||||
|
} else {
|
||||||
|
throw new Error('DownloadKubectlFailed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedToolpath = await toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + getExecutableExtension(), kubectlToolName, version);
|
cachedToolpath = await toolCache.cacheFile(kubectlDownloadPath, kubectlToolName + getExecutableExtension(), kubectlToolName, version);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { IExecSyncResult } from '../../utilities/tool-runner';
|
|||||||
|
|
||||||
import { deployPodCanary } from './pod-canary-deployment-helper';
|
import { deployPodCanary } from './pod-canary-deployment-helper';
|
||||||
import { deploySMICanary } from './smi-canary-deployment-helper';
|
import { deploySMICanary } from './smi-canary-deployment-helper';
|
||||||
import { checkForErrors, annotateChildPods, annotateNamespace } from "../utility";
|
import { checkForErrors, annotateChildPods, getWorkflowFilePath, getLastSuccessfulRunSha, DeploymentConfig, getDeploymentConfig, normaliseWorkflowStrLabel } from "../utility";
|
||||||
import { isBlueGreenDeploymentStrategy, isIngressRoute, isSMIRoute, routeBlueGreen } from './blue-green-helper';
|
import { isBlueGreenDeploymentStrategy, isIngressRoute, isSMIRoute, routeBlueGreen } from './blue-green-helper';
|
||||||
import { deployBlueGreenService } from './service-blue-green-helper';
|
import { deployBlueGreenService } from './service-blue-green-helper';
|
||||||
import { deployBlueGreenIngress } from './ingress-blue-green-helper';
|
import { deployBlueGreenIngress } from './ingress-blue-green-helper';
|
||||||
@@ -45,7 +45,7 @@ export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], depl
|
|||||||
ingressResources.forEach(ingressResource => {
|
ingressResources.forEach(ingressResource => {
|
||||||
kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name);
|
kubectl.getResource(KubernetesConstants.DiscoveryAndLoadBalancerResource.ingress, ingressResource.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
// annotate resources
|
// annotate resources
|
||||||
let allPods: any;
|
let allPods: any;
|
||||||
try {
|
try {
|
||||||
@@ -54,7 +54,7 @@ export async function deploy(kubectl: Kubectl, manifestFilePaths: string[], depl
|
|||||||
core.debug("Unable to parse pods; Error: " + e);
|
core.debug("Unable to parse pods; Error: " + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
annotateResources(deployedManifestFiles, kubectl, resourceTypes, allPods);
|
annotateAndLabelResources(deployedManifestFiles, kubectl, resourceTypes, allPods);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getManifestFiles(manifestFilePaths: string[]): string[] {
|
export function getManifestFiles(manifestFilePaths: string[]): string[] {
|
||||||
@@ -79,7 +79,7 @@ function deployManifests(files: string[], kubectl: Kubectl, isCanaryDeploymentSt
|
|||||||
result = canaryDeploymentOutput.result;
|
result = canaryDeploymentOutput.result;
|
||||||
files = canaryDeploymentOutput.newFilePaths;
|
files = canaryDeploymentOutput.newFilePaths;
|
||||||
} else if (isBlueGreenDeploymentStrategy) {
|
} else if (isBlueGreenDeploymentStrategy) {
|
||||||
let blueGreenDeploymentOutput: any;
|
let blueGreenDeploymentOutput: any;
|
||||||
if (isIngressRoute()) {
|
if (isIngressRoute()) {
|
||||||
blueGreenDeploymentOutput = deployBlueGreenIngress(kubectl, files);
|
blueGreenDeploymentOutput = deployBlueGreenIngress(kubectl, files);
|
||||||
} else if (isSMIRoute()) {
|
} else if (isSMIRoute()) {
|
||||||
@@ -128,19 +128,34 @@ async function checkManifestStability(kubectl: Kubectl, resources: Resource[]):
|
|||||||
await KubernetesManifestUtility.checkManifestStability(kubectl, resources);
|
await KubernetesManifestUtility.checkManifestStability(kubectl, resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
function annotateResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any) {
|
async function annotateAndLabelResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any) {
|
||||||
|
const workflowFilePath = await getWorkflowFilePath(TaskInputParameters.githubToken);
|
||||||
|
const deploymentConfig = await getDeploymentConfig();
|
||||||
|
const annotationKeyLabel = models.getWorkflowAnnotationKeyLabel(workflowFilePath);
|
||||||
|
annotateResources(files, kubectl, resourceTypes, allPods, annotationKeyLabel, workflowFilePath, deploymentConfig);
|
||||||
|
labelResources(files, kubectl, annotationKeyLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function annotateResources(files: string[], kubectl: Kubectl, resourceTypes: Resource[], allPods: any, annotationKey: string, workflowFilePath: string, deploymentConfig: DeploymentConfig) {
|
||||||
const annotateResults: IExecSyncResult[] = [];
|
const annotateResults: IExecSyncResult[] = [];
|
||||||
annotateResults.push(annotateNamespace(kubectl, TaskInputParameters.namespace));
|
const lastSuccessSha = getLastSuccessfulRunSha(kubectl, TaskInputParameters.namespace, annotationKey);
|
||||||
annotateResults.push(kubectl.annotateFiles(files, models.workflowAnnotations, true));
|
let annotationKeyValStr = annotationKey + '=' + models.getWorkflowAnnotationsJson(lastSuccessSha, workflowFilePath, deploymentConfig);
|
||||||
|
annotateResults.push(kubectl.annotate('namespace', TaskInputParameters.namespace, annotationKeyValStr));
|
||||||
|
annotateResults.push(kubectl.annotateFiles(files, annotationKeyValStr));
|
||||||
resourceTypes.forEach(resource => {
|
resourceTypes.forEach(resource => {
|
||||||
if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) {
|
if (resource.type.toUpperCase() !== models.KubernetesWorkload.pod.toUpperCase()) {
|
||||||
annotateChildPods(kubectl, resource.type, resource.name, allPods)
|
annotateChildPods(kubectl, resource.type, resource.name, annotationKeyValStr, allPods)
|
||||||
.forEach(execResult => annotateResults.push(execResult));
|
.forEach(execResult => annotateResults.push(execResult));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
checkForErrors(annotateResults, true);
|
checkForErrors(annotateResults, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function labelResources(files: string[], kubectl: Kubectl, label: string) {
|
||||||
|
const labels = [`workflowFriendlyName=${normaliseWorkflowStrLabel(process.env.GITHUB_WORKFLOW)}`, `workflow=${label}`];
|
||||||
|
checkForErrors([kubectl.labelFiles(files, labels)], true);
|
||||||
|
}
|
||||||
|
|
||||||
function isCanaryDeploymentStrategy(deploymentStrategy: string): boolean {
|
function isCanaryDeploymentStrategy(deploymentStrategy: string): boolean {
|
||||||
return deploymentStrategy != null && deploymentStrategy.toUpperCase() === canaryDeploymentHelper.CANARY_DEPLOYMENT_STRATEGY.toUpperCase();
|
return deploymentStrategy != null && deploymentStrategy.toUpperCase() === canaryDeploymentHelper.CANARY_DEPLOYMENT_STRATEGY.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export async function rejectBlueGreenIngress(kubectl: Kubectl, filePaths: string
|
|||||||
export function routeBlueGreenIngress(kubectl: Kubectl, nextLabel: string, serviceNameMap: Map<string, string>, ingressEntityList: any[]) {
|
export function routeBlueGreenIngress(kubectl: Kubectl, nextLabel: string, serviceNameMap: Map<string, string>, ingressEntityList: any[]) {
|
||||||
let newObjectsList = [];
|
let newObjectsList = [];
|
||||||
if (!nextLabel) {
|
if (!nextLabel) {
|
||||||
newObjectsList = newObjectsList.concat(ingressEntityList);
|
newObjectsList = ingressEntityList.filter(ingress => isIngressRouted(ingress, serviceNameMap));
|
||||||
} else {
|
} else {
|
||||||
ingressEntityList.forEach((inputObject) => {
|
ingressEntityList.forEach((inputObject) => {
|
||||||
if (isIngressRouted(inputObject, serviceNameMap)) {
|
if (isIngressRouted(inputObject, serviceNameMap)) {
|
||||||
|
|||||||
@@ -177,9 +177,9 @@ export function validateTrafficSplitsState(kubectl: Kubectl, serviceEntityList:
|
|||||||
export function cleanupSMI(kubectl: Kubectl, serviceEntityList: any[]) {
|
export function cleanupSMI(kubectl: Kubectl, serviceEntityList: any[]) {
|
||||||
const deleteList = [];
|
const deleteList = [];
|
||||||
serviceEntityList.forEach((serviceObject) => {
|
serviceEntityList.forEach((serviceObject) => {
|
||||||
|
deleteList.push({ name: getBlueGreenResourceName(serviceObject.metadata.name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), kind: TRAFFIC_SPLIT_OBJECT });
|
||||||
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, GREEN_SUFFIX), kind: serviceObject.kind});
|
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, GREEN_SUFFIX), kind: serviceObject.kind});
|
||||||
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, STABLE_SUFFIX), kind: serviceObject.kind});
|
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, STABLE_SUFFIX), kind: serviceObject.kind});
|
||||||
deleteList.push({name: getBlueGreenResourceName(serviceObject.metadata.name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX), kind: TRAFFIC_SPLIT_OBJECT});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// deleting all objects
|
// deleting all objects
|
||||||
|
|||||||
@@ -186,33 +186,31 @@ function getTrafficSplitObject(kubectl: Kubectl, name: string, stableWeight: num
|
|||||||
if (!trafficSplitAPIVersion) {
|
if (!trafficSplitAPIVersion) {
|
||||||
trafficSplitAPIVersion = kubectlUtils.getTrafficSplitAPIVersion(kubectl);
|
trafficSplitAPIVersion = kubectlUtils.getTrafficSplitAPIVersion(kubectl);
|
||||||
}
|
}
|
||||||
const trafficSplitObjectJson = `{
|
|
||||||
|
return `{
|
||||||
"apiVersion": "${trafficSplitAPIVersion}",
|
"apiVersion": "${trafficSplitAPIVersion}",
|
||||||
"kind": "TrafficSplit",
|
"kind": "TrafficSplit",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"name": "%s"
|
"name": "${getTrafficSplitResourceName(name)}"
|
||||||
},
|
},
|
||||||
"spec": {
|
"spec": {
|
||||||
"backends": [
|
"backends": [
|
||||||
{
|
{
|
||||||
"service": "%s",
|
"service": "${canaryDeploymentHelper.getStableResourceName(name)}",
|
||||||
"weight": "%sm"
|
"weight": "${stableWeight}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"service": "%s",
|
"service": "${canaryDeploymentHelper.getBaselineResourceName(name)}",
|
||||||
"weight": "%sm"
|
"weight": "${baselineWeight}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"service": "%s",
|
"service": "${canaryDeploymentHelper.getCanaryResourceName(name)}",
|
||||||
"weight": "%sm"
|
"weight": "${canaryWeight}"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"service": "%s"
|
"service": "${name}"
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
const trafficSplitObject = util.format(trafficSplitObjectJson, getTrafficSplitResourceName(name), canaryDeploymentHelper.getStableResourceName(name), stableWeight, canaryDeploymentHelper.getBaselineResourceName(name), baselineWeight, canaryDeploymentHelper.getCanaryResourceName(name), canaryWeight, name);
|
|
||||||
return trafficSplitObject;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTrafficSplitResourceName(name: string) {
|
function getTrafficSplitResourceName(name: string) {
|
||||||
|
|||||||
+136
-21
@@ -2,7 +2,17 @@ import * as os from 'os';
|
|||||||
import * as core from '@actions/core';
|
import * as core from '@actions/core';
|
||||||
import { IExecSyncResult } from './tool-runner';
|
import { IExecSyncResult } from './tool-runner';
|
||||||
import { Kubectl } from '../kubectl-object-model';
|
import { Kubectl } from '../kubectl-object-model';
|
||||||
import { workflowAnnotations } from '../constants';
|
import { GitHubClient } from '../githubClient';
|
||||||
|
import { StatusCodes } from "./httpClient";
|
||||||
|
import * as inputParams from "../input-parameters";
|
||||||
|
import { DockerExec } from '../docker-object-model';
|
||||||
|
import * as io from '@actions/io';
|
||||||
|
|
||||||
|
export interface DeploymentConfig {
|
||||||
|
manifestFilePaths: string[];
|
||||||
|
helmChartFilePaths: string[];
|
||||||
|
dockerfilePaths: any;
|
||||||
|
}
|
||||||
|
|
||||||
export function getExecutableExtension(): string {
|
export function getExecutableExtension(): string {
|
||||||
if (os.type().match(/^Win/)) {
|
if (os.type().match(/^Win/)) {
|
||||||
@@ -50,7 +60,61 @@ export function checkForErrors(execResults: IExecSyncResult[], warnIfError?: boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function annotateChildPods(kubectl: Kubectl, resourceType: string, resourceName: string, allPods): IExecSyncResult[] {
|
export function getLastSuccessfulRunSha(kubectl: Kubectl, namespaceName: string, annotationKey: string): string {
|
||||||
|
try {
|
||||||
|
const result = kubectl.getResource('namespace', namespaceName);
|
||||||
|
if (result) {
|
||||||
|
if (result.stderr) {
|
||||||
|
core.warning(`${result.stderr}`);
|
||||||
|
return process.env.GITHUB_SHA;
|
||||||
|
}
|
||||||
|
else if (result.stdout) {
|
||||||
|
const annotationsSet = JSON.parse(result.stdout).metadata.annotations;
|
||||||
|
if (annotationsSet && annotationsSet[annotationKey]) {
|
||||||
|
return JSON.parse(annotationsSet[annotationKey].replace(/'/g, '"')).commit;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 'NA';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
core.warning(`Failed to get commits from cluster. ${JSON.stringify(ex)}`);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getWorkflowFilePath(githubToken: string): Promise<string> {
|
||||||
|
let workflowFilePath = process.env.GITHUB_WORKFLOW;
|
||||||
|
if (!workflowFilePath.startsWith('.github/workflows/')) {
|
||||||
|
const githubClient = new GitHubClient(process.env.GITHUB_REPOSITORY, githubToken);
|
||||||
|
const response = await githubClient.getWorkflows();
|
||||||
|
if (response) {
|
||||||
|
if (response.statusCode == StatusCodes.OK
|
||||||
|
&& response.body
|
||||||
|
&& response.body.total_count) {
|
||||||
|
if (response.body.total_count > 0) {
|
||||||
|
for (let workflow of response.body.workflows) {
|
||||||
|
if (process.env.GITHUB_WORKFLOW === workflow.name) {
|
||||||
|
workflowFilePath = workflow.path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (response.statusCode != StatusCodes.OK) {
|
||||||
|
core.debug(`An error occured while getting list of workflows on the repo. Statuscode: ${response.statusCode}, StatusMessage: ${response.statusMessage}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.warning(`Failed to get response from workflow list API`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve(workflowFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function annotateChildPods(kubectl: Kubectl, resourceType: string, resourceName: string, annotationKeyValStr: string, allPods): IExecSyncResult[] {
|
||||||
const commandExecutionResults = [];
|
const commandExecutionResults = [];
|
||||||
let owner = resourceName;
|
let owner = resourceName;
|
||||||
if (resourceType.toLowerCase().indexOf('deployment') > -1) {
|
if (resourceType.toLowerCase().indexOf('deployment') > -1) {
|
||||||
@@ -63,7 +127,7 @@ export function annotateChildPods(kubectl: Kubectl, resourceType: string, resour
|
|||||||
if (owners) {
|
if (owners) {
|
||||||
for (let ownerRef of owners) {
|
for (let ownerRef of owners) {
|
||||||
if (ownerRef.name === owner) {
|
if (ownerRef.name === owner) {
|
||||||
commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, workflowAnnotations, true));
|
commandExecutionResults.push(kubectl.annotate('pod', pod.metadata.name, annotationKeyValStr));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,27 +138,41 @@ export function annotateChildPods(kubectl: Kubectl, resourceType: string, resour
|
|||||||
return commandExecutionResults;
|
return commandExecutionResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function annotateNamespace(kubectl: Kubectl, namespaceName: string): IExecSyncResult {
|
export async function getDeploymentConfig(): Promise<DeploymentConfig> {
|
||||||
const result = kubectl.getResource('namespace', namespaceName);
|
|
||||||
if (!result) {
|
|
||||||
return { code: 1, stderr: 'Failed to get resource' } as IExecSyncResult;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (result.stderr) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (result.stdout) {
|
let helmChartPaths: string[] = (process.env.HELM_CHART_PATHS && process.env.HELM_CHART_PATHS.split(';').filter(path => path != "")) || [];
|
||||||
const annotationsSet = JSON.parse(result.stdout).metadata.annotations;
|
helmChartPaths = helmChartPaths.map(helmchart => getNormalizedPath(helmchart.trim()));
|
||||||
if (annotationsSet && annotationsSet.runUri) {
|
|
||||||
if (annotationsSet.runUri.indexOf(process.env['GITHUB_REPOSITORY']) == -1) {
|
let inputManifestFiles: string[] = inputParams.manifests || [];
|
||||||
core.debug(`Skipping 'annotate namespace' as namespace annotated by other workflow`);
|
if (!helmChartPaths || helmChartPaths.length == 0) {
|
||||||
return { code: 0, stdout: '' } as IExecSyncResult;
|
inputManifestFiles = inputManifestFiles.map(manifestFile => getNormalizedPath(manifestFile));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return kubectl.annotate('namespace', namespaceName, workflowAnnotations, true);
|
const imageNames = inputParams.containers || [];
|
||||||
|
let imageDockerfilePathMap: { [id: string]: string; } = {};
|
||||||
|
|
||||||
|
//Fetching from image label if available
|
||||||
|
for (const image of imageNames) {
|
||||||
|
try {
|
||||||
|
imageDockerfilePathMap[image] = await getDockerfilePath(image);
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
core.warning(`Failed to get dockerfile path for image ${image.toString()} | ` + ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deploymentConfig = <DeploymentConfig>{
|
||||||
|
manifestFilePaths: inputManifestFiles,
|
||||||
|
helmChartFilePaths: helmChartPaths,
|
||||||
|
dockerfilePaths: imageDockerfilePathMap
|
||||||
|
};
|
||||||
|
return Promise.resolve(deploymentConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normaliseWorkflowStrLabel(workflowName: string): string {
|
||||||
|
workflowName = workflowName.startsWith('.github/workflows/') ?
|
||||||
|
workflowName.replace(".github/workflows/", "") : workflowName;
|
||||||
|
return workflowName.replace(/ /g, "_");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sleep(timeout: number) {
|
export function sleep(timeout: number) {
|
||||||
@@ -108,3 +186,40 @@ export function getRandomInt(max: number) {
|
|||||||
export function getCurrentTime(): number {
|
export function getCurrentTime(): number {
|
||||||
return new Date().getTime();
|
return new Date().getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkDockerPath() {
|
||||||
|
let dockerPath = await io.which('docker', false);
|
||||||
|
if (!dockerPath) {
|
||||||
|
throw new Error('Docker is not installed.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDockerfilePath(image: any): Promise<string> {
|
||||||
|
let imageConfig: any, imageInspectResult: string;
|
||||||
|
var dockerExec: DockerExec = new DockerExec('docker');
|
||||||
|
await checkDockerPath();
|
||||||
|
dockerExec.pull(image, [], true);
|
||||||
|
imageInspectResult = dockerExec.inspect(image, [], true);
|
||||||
|
imageConfig = JSON.parse(imageInspectResult)[0];
|
||||||
|
const DOCKERFILE_PATH_LABEL_KEY = 'dockerfile-path';
|
||||||
|
let pathValue: string = '';
|
||||||
|
if (imageConfig) {
|
||||||
|
if ((imageConfig.Config) && (imageConfig.Config.Labels) && (imageConfig.Config.Labels[DOCKERFILE_PATH_LABEL_KEY])) {
|
||||||
|
const pathLabel = imageConfig.Config.Labels[DOCKERFILE_PATH_LABEL_KEY];
|
||||||
|
pathValue = getNormalizedPath(pathLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve(pathValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isHttpUrl(url: string) {
|
||||||
|
const HTTP_REGEX = /^https?:\/\/.*$/;
|
||||||
|
return HTTP_REGEX.test(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNormalizedPath(pathValue: string) {
|
||||||
|
if (!isHttpUrl(pathValue)) { //if it is not an http url then convert to link from current repo and commit
|
||||||
|
return `https://github.com/${process.env.GITHUB_REPOSITORY}/blob/${process.env.GITHUB_SHA}/${pathValue}`;
|
||||||
|
}
|
||||||
|
return pathValue;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user