Compare commits

..

1 Commits

Author SHA1 Message Date
Oliver King 4fd4e1ad9d Fix README.md typo 2022-08-22 11:06:05 -04:00
12 changed files with 113 additions and 247 deletions
-36
View File
@@ -1,36 +0,0 @@
name: Bug Report
description: File a bug report specifying all inputs you provided for the action, we will respond to this thread with any questions.
title: 'Bug: '
labels: ['bug', 'triage']
assignees: '@Azure/aks-atlanta'
body:
- type: textarea
id: What-happened
attributes:
label: What happened?
description: Tell us what happened and how is it different from the expected?
placeholder: Tell us what you see!
validations:
required: true
- type: checkboxes
id: Version
attributes:
label: Version
options:
- label: I am using the latest version
required: true
- type: input
id: Runner
attributes:
label: Runner
description: What runner are you using?
placeholder: Mention the runner info (self-hosted, operating system)
validations:
required: true
- type: textarea
id: Logs
attributes:
label: Relevant log output
description: Run in debug mode for the most verbose logs. Please feel free to attach a screenshot of the logs
validations:
required: true
-6
View File
@@ -1,6 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: GitHub Action "k8s-deploy" Support
url: https://github.com/Azure/k8s-deploy
security: https://github.com/Azure/k8s-deploy/blob/main/SECURITY.md
about: Please ask and answer questions here.
@@ -1,13 +0,0 @@
name: Feature Request
description: File a Feature Request form, we will respond to this thread with any questions.
title: 'Feature Request: '
labels: ['Feature']
assignees: '@Azure/aks-atlanta'
body:
- type: textarea
id: Feature_request
attributes:
label: Feature request
description: Provide example functionality and links to relevant docs
validations:
required: true
+2 -15
View File
@@ -4,15 +4,6 @@ This action is used to deploy manifests to Kubernetes clusters. It requires that
If you are looking to automate your workflows to deploy to [Azure Web Apps](https://azure.microsoft.com/en-us/services/app-service/web/) and [Azure Web App for Containers](https://azure.microsoft.com/en-us/services/app-service/containers/), consider using [`Azure/webapps-deploy`](https://github.com/Azure/webapps-deploy) action. If you are looking to automate your workflows to deploy to [Azure Web Apps](https://azure.microsoft.com/en-us/services/app-service/web/) and [Azure Web App for Containers](https://azure.microsoft.com/en-us/services/app-service/containers/), consider using [`Azure/webapps-deploy`](https://github.com/Azure/webapps-deploy) action.
This action requires the following permissions from your workflow:
```yaml
permissions:
id-token: write
contents: read
actions: read
```
## Action capabilities ## Action capabilities
Following are the key capabilities of this action: Following are the key capabilities of this action:
@@ -91,8 +82,8 @@ Following are the key capabilities of this action:
<td>Used to compute the number of replicas of &#39;-baseline&#39; and &#39;-canary&#39; variants of the workloads found in manifest files. For the specified percentage input, if (percentage * numberOfDesirerdReplicas)/100 is not a round number, the floor of this number is used while creating &#39;-baseline&#39; and &#39;-canary&#39;.<br/><br/>For example, if Deployment hello-world was found in the input manifest file with &#39;replicas: 4&#39; and if &#39;strategy: canary&#39; and &#39;percentage: 25&#39; are given as inputs to the action, then the Deployments hello-world-baseline and hello-world-canary are created with 1 replica each. The &#39;-baseline&#39; variant is created with the same image and tag as the stable version (4 replica variant prior to deployment) while the &#39;-canary&#39; variant is created with the image and tag corresponding to the new changes being deployed</td> <td>Used to compute the number of replicas of &#39;-baseline&#39; and &#39;-canary&#39; variants of the workloads found in manifest files. For the specified percentage input, if (percentage * numberOfDesirerdReplicas)/100 is not a round number, the floor of this number is used while creating &#39;-baseline&#39; and &#39;-canary&#39;.<br/><br/>For example, if Deployment hello-world was found in the input manifest file with &#39;replicas: 4&#39; and if &#39;strategy: canary&#39; and &#39;percentage: 25&#39; are given as inputs to the action, then the Deployments hello-world-baseline and hello-world-canary are created with 1 replica each. The &#39;-baseline&#39; variant is created with the same image and tag as the stable version (4 replica variant prior to deployment) while the &#39;-canary&#39; variant is created with the image and tag corresponding to the new changes being deployed</td>
</tr> </tr>
<tr> <tr>
<td>baseline-and-canary-replicas </br></br> (Optional and relevant only if strategy is canary and traffic-split-method is smi)</td> <td>baseline-and-canary-replicas </br></br> (Optional and relevant only if traffic-split-method is canary)</td>
<td>The number of baseline and canary replicas. Percentage traffic split is controlled in the service mesh plane, the actual number of replicas for canary and baseline variants could be controlled independently of the traffic split. For example, assume that the input Deployment manifest desired 30 replicas to be used for stable and that the following inputs were specified for the action </br></br><code>&nbsp;&nbsp;&nbsp;&nbsp;strategy: canary<br>&nbsp;&nbsp;&nbsp;&nbsp;trafficSplitMethod: smi<br>&nbsp;&nbsp;&nbsp;&nbsp;percentage: 20<br>&nbsp;&nbsp;&nbsp;&nbsp;baselineAndCanaryReplicas: 1</code></br></br> In this case, stable variant will receive 80% traffic while baseline and canary variants will receive 10% each (20% split equally between baseline and canary). However, instead of creating baseline and canary with 3 replicas each, the explicit count of baseline and canary replicas is honored. That is, only 1 replica each is created for baseline and canary variants.</td> <td>The number of baseline and canary replicas. Percentage traffic split is controlled in the service mesh plane, the actual number of replicas for canary and baseline variants could be controlled independently of the traffic split. For example, assume that the input Deployment manifest desired 30 replicas to be used for stable and that the following inputs were specified for the action </br></br><code>&nbsp;&nbsp;&nbsp;&nbsp;strategy: canary<br>&nbsp;&nbsp;&nbsp;&nbsp;trafficSplitMethod: smi<br>&nbsp;&nbsp;&nbsp;&nbsp;percentage: 20<br>&nbsp;&nbsp;&nbsp;&nbsp;baselineAndCanaryReplicas: 1</code></br></br> In this case, stable variant will receive 80% traffic while baseline and canary variants will receive 10% each (20% split equally between baseline and canary). However, instead of creating baseline and canary with 3 replicas, the explicit count of baseline and canary replicas is honored. That is, only 1 replica each is created for baseline and canary variants.</td>
</tr> </tr>
<tr> <tr>
<td>route-method </br></br>(Optional and relevant only if strategy is blue-green)</td> <td>route-method </br></br>(Optional and relevant only if strategy is blue-green)</td>
@@ -471,7 +462,3 @@ provided by the bot. You will only need to do this once across all repos using o
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 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.
## Support
k8s-deploy is an open source project that is [**not** covered by the Microsoft Azure support policy](https://support.microsoft.com/en-us/help/2941892/support-for-linux-and-open-source-technology-in-azure). [Please search open issues here](https://github.com/Azure/k8s-deploy/issues), and if your issue isn't already represented please [open a new one](https://github.com/Azure/k8s-deploy/issues/new/choose). The project maintainers will respond to the best of their abilities.
+1 -1
View File
@@ -41,7 +41,7 @@ inputs:
baseline-and-canary-replicas: baseline-and-canary-replicas:
description: 'Baseline and canary replicas count. Valid value between 0 to 100 (inclusive)' description: 'Baseline and canary replicas count. Valid value between 0 to 100 (inclusive)'
required: false required: false
default: '' default: 0
percentage: percentage:
description: 'Percentage of traffic redirect to canary deployment' description: 'Percentage of traffic redirect to canary deployment'
required: false required: false
+15 -26
View File
@@ -9,7 +9,7 @@
"version": "0.0.0", "version": "0.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/core": "^1.9.1", "@actions/core": "^1.2.6",
"@actions/exec": "^1.0.0", "@actions/exec": "^1.0.0",
"@actions/io": "^1.0.0", "@actions/io": "^1.0.0",
"@actions/tool-cache": "1.1.2", "@actions/tool-cache": "1.1.2",
@@ -29,20 +29,11 @@
} }
}, },
"node_modules/@actions/core": { "node_modules/@actions/core": {
"version": "1.9.1", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.0.tgz",
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "integrity": "sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==",
"dependencies": { "dependencies": {
"@actions/http-client": "^2.0.1", "@actions/http-client": "^2.0.1"
"uuid": "^8.3.2"
}
},
"node_modules/@actions/core/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
} }
}, },
"node_modules/@actions/exec": { "node_modules/@actions/exec": {
@@ -6416,19 +6407,11 @@
}, },
"dependencies": { "dependencies": {
"@actions/core": { "@actions/core": {
"version": "1.9.1", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.1.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.0.tgz",
"integrity": "sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA==", "integrity": "sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==",
"requires": { "requires": {
"@actions/http-client": "^2.0.1", "@actions/http-client": "^2.0.1"
"uuid": "^8.3.2"
},
"dependencies": {
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
}
} }
}, },
"@actions/exec": { "@actions/exec": {
@@ -7380,6 +7363,12 @@
"integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
"dev": true "dev": true
}, },
"@vercel/ncc": {
"version": "0.34.0",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz",
"integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==",
"dev": true
},
"abab": { "abab": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+1 -1
View File
@@ -11,7 +11,7 @@
"format-check": "prettier --check ." "format-check": "prettier --check ."
}, },
"dependencies": { "dependencies": {
"@actions/core": "^1.9.1", "@actions/core": "^1.2.6",
"@actions/exec": "^1.0.0", "@actions/exec": "^1.0.0",
"@actions/io": "^1.0.0", "@actions/io": "^1.0.0",
"@actions/tool-cache": "1.1.2", "@actions/tool-cache": "1.1.2",
+5 -17
View File
@@ -1,7 +1,7 @@
import * as core from '@actions/core' import * as core from '@actions/core'
import * as deploy from './deploy'
import * as canaryDeploymentHelper from '../strategyHelpers/canary/canaryHelper' import * as canaryDeploymentHelper from '../strategyHelpers/canary/canaryHelper'
import * as SMICanaryDeploymentHelper from '../strategyHelpers/canary/smiCanaryHelper' import * as SMICanaryDeploymentHelper from '../strategyHelpers/canary/smiCanaryHelper'
import * as PodCanaryHelper from '../strategyHelpers/canary/podCanaryHelper'
import { import {
getResources, getResources,
updateManifestFiles updateManifestFiles
@@ -57,8 +57,6 @@ export async function promote(
async function promoteCanary(kubectl: Kubectl, manifests: string[]) { async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
let includeServices = false let includeServices = false
const manifestFilesForDeployment: string[] = updateManifestFiles(manifests)
const trafficSplitMethod = parseTrafficSplitMethod( const trafficSplitMethod = parseTrafficSplitMethod(
core.getInput('traffic-split-method', {required: true}) core.getInput('traffic-split-method', {required: true})
) )
@@ -74,14 +72,8 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
) )
core.endGroup() core.endGroup()
core.startGroup( core.startGroup('Deploying input manifests with SMI canary strategy')
'Deploying input manifests with SMI canary strategy from promote' await deploy.deploy(kubectl, manifests, DeploymentStrategy.CANARY)
)
await SMICanaryDeploymentHelper.deploySMICanary(
manifestFilesForDeployment,
kubectl,
true
)
core.endGroup() core.endGroup()
core.startGroup('Redirecting traffic to stable deployment') core.startGroup('Redirecting traffic to stable deployment')
@@ -91,12 +83,8 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
) )
core.endGroup() core.endGroup()
} else { } else {
core.startGroup('Deploying input manifests from promote') core.startGroup('Deploying input manifests')
await PodCanaryHelper.deployPodCanary( await deploy.deploy(kubectl, manifests, DeploymentStrategy.CANARY)
manifestFilesForDeployment,
kubectl,
true
)
core.endGroup() core.endGroup()
} }
+4 -30
View File
@@ -2,7 +2,6 @@ import {Kubectl} from '../../types/kubectl'
import * as fs from 'fs' import * as fs from 'fs'
import * as yaml from 'js-yaml' import * as yaml from 'js-yaml'
import * as core from '@actions/core' import * as core from '@actions/core'
import {ExecOutput} from '@actions/exec'
import { import {
isDeploymentEntity, isDeploymentEntity,
isServiceEntity, isServiceEntity,
@@ -31,7 +30,7 @@ export async function deleteCanaryDeployment(
includeServices: boolean includeServices: boolean
) { ) {
if (manifestFilePaths == null || manifestFilePaths.length == 0) { if (manifestFilePaths == null || manifestFilePaths.length == 0) {
throw new Error('Manifest files for deleting canary deployment not found') throw new Error('Manifest file not found')
} }
await cleanUpCanary(kubectl, manifestFilePaths, includeServices) await cleanUpCanary(kubectl, manifestFilePaths, includeServices)
@@ -55,7 +54,7 @@ export function isResourceMarkedAsStable(inputObject: any): boolean {
export function getStableResource(inputObject: any): object { export function getStableResource(inputObject: any): object {
const replicaCount = specContainsReplicas(inputObject.kind) const replicaCount = specContainsReplicas(inputObject.kind)
? inputObject.spec.replicas ? inputObject.metadata.replicas
: 0 : 0
return getNewCanaryObject(inputObject, replicaCount, STABLE_LABEL_VALUE) return getNewCanaryObject(inputObject, replicaCount, STABLE_LABEL_VALUE)
@@ -80,12 +79,7 @@ export async function fetchResource(
kind: string, kind: string,
name: string name: string
) { ) {
let result: ExecOutput const result = await kubectl.getResource(kind, name)
try {
result = await kubectl.getResource(kind, name)
} catch (e) {
core.debug(`detected error while fetching resources: ${e}`)
}
if (!result || result?.stderr) { if (!result || result?.stderr) {
return null return null
@@ -99,7 +93,7 @@ export async function fetchResource(
return resource return resource
} catch (ex) { } catch (ex) {
core.debug( core.debug(
`Exception occurred while parsing ${resource} in JSON object: ${ex}` `Exception occurred while Parsing ${resource} in JSON object: ${ex}`
) )
} }
} }
@@ -117,26 +111,6 @@ export function getStableResourceName(name: string) {
return name + STABLE_SUFFIX return name + STABLE_SUFFIX
} }
export function getBaselineDeploymentFromStableDeployment(
inputObject: any,
replicaCount: number
): object {
// TODO: REFACTOR TO MAKE EVERYTHING TYPE SAFE
const oldName = inputObject.metadata.name
const newName =
oldName.substring(0, oldName.length - STABLE_SUFFIX.length) +
BASELINE_SUFFIX
const newObject = getNewCanaryObject(
inputObject,
replicaCount,
BASELINE_LABEL_VALUE
) as any
newObject.metadata.name = newName
return newObject
}
function getNewCanaryObject( function getNewCanaryObject(
inputObject: any, inputObject: any,
replicas: number, replicas: number,
+32 -22
View File
@@ -8,13 +8,9 @@ import * as canaryDeploymentHelper from './canaryHelper'
import {isDeploymentEntity} from '../../types/kubernetesTypes' import {isDeploymentEntity} from '../../types/kubernetesTypes'
import {getReplicaCount} from '../../utilities/manifestUpdateUtils' import {getReplicaCount} from '../../utilities/manifestUpdateUtils'
export async function deployPodCanary( export async function deployPodCanary(filePaths: string[], kubectl: Kubectl) {
filePaths: string[],
kubectl: Kubectl,
onlyDeployStable: boolean = false
) {
const newObjectsList = [] const newObjectsList = []
const percentage = parseInt(core.getInput('percentage', {required: true})) const percentage = parseInt(core.getInput('percentage'))
if (percentage < 0 || percentage > 100) if (percentage < 0 || percentage > 100)
throw Error('Percentage must be between 0 and 100') throw Error('Percentage must be between 0 and 100')
@@ -26,7 +22,7 @@ export async function deployPodCanary(
const name = inputObject.metadata.name const name = inputObject.metadata.name
const kind = inputObject.kind const kind = inputObject.kind
if (!onlyDeployStable && isDeploymentEntity(kind)) { if (isDeploymentEntity(kind)) {
core.debug('Calculating replica count for canary') core.debug('Calculating replica count for canary')
const canaryReplicaCount = calculateReplicaCountForCanary( const canaryReplicaCount = calculateReplicaCountForCanary(
inputObject, inputObject,
@@ -34,22 +30,37 @@ export async function deployPodCanary(
) )
core.debug('Replica count is ' + canaryReplicaCount) core.debug('Replica count is ' + canaryReplicaCount)
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource( // Get stable object
inputObject, core.debug('Querying stable object')
canaryReplicaCount
)
newObjectsList.push(newCanaryObject)
// if there's already a stable object, deploy baseline as well
const stableObject = await canaryDeploymentHelper.fetchResource( const stableObject = await canaryDeploymentHelper.fetchResource(
kubectl, kubectl,
kind, kind,
name name
) )
if (stableObject) {
if (!stableObject) {
core.debug('Stable object not found. Creating canary object')
const newCanaryObject =
canaryDeploymentHelper.getNewCanaryResource(
inputObject,
canaryReplicaCount
)
newObjectsList.push(newCanaryObject)
} else {
core.debug( core.debug(
`Stable object found for ${kind} ${name}. Creating baseline objects` 'Creating canary and baseline objects. Stable object found: ' +
JSON.stringify(stableObject)
) )
const newCanaryObject =
canaryDeploymentHelper.getNewCanaryResource(
inputObject,
canaryReplicaCount
)
core.debug(
'New canary object: ' + JSON.stringify(newCanaryObject)
)
const newBaselineObject = const newBaselineObject =
canaryDeploymentHelper.getNewBaselineResource( canaryDeploymentHelper.getNewBaselineResource(
stableObject, stableObject,
@@ -58,10 +69,12 @@ export async function deployPodCanary(
core.debug( core.debug(
'New baseline object: ' + JSON.stringify(newBaselineObject) 'New baseline object: ' + JSON.stringify(newBaselineObject)
) )
newObjectsList.push(newCanaryObject)
newObjectsList.push(newBaselineObject) newObjectsList.push(newBaselineObject)
} }
} else { } else {
// deploy non deployment entity or regular deployments for promote as they are // update non deployment entity as it is
newObjectsList.push(inputObject) newObjectsList.push(inputObject)
} }
} }
@@ -75,10 +88,7 @@ export async function deployPodCanary(
return {result, newFilePaths: manifestFiles} return {result, newFilePaths: manifestFiles}
} }
export function calculateReplicaCountForCanary( function calculateReplicaCountForCanary(inputObject: any, percentage: number) {
inputObject: any,
percentage: number
) {
const inputReplicaCount = getReplicaCount(inputObject) const inputReplicaCount = getReplicaCount(inputObject)
return Math.max(1, Math.round((inputReplicaCount * percentage) / 100)) return Math.round((inputReplicaCount * percentage) / 100)
} }
+45 -66
View File
@@ -6,7 +6,6 @@ import * as yaml from 'js-yaml'
import * as fileHelper from '../../utilities/fileUtils' import * as fileHelper from '../../utilities/fileUtils'
import * as kubectlUtils from '../../utilities/trafficSplitUtils' import * as kubectlUtils from '../../utilities/trafficSplitUtils'
import * as canaryDeploymentHelper from './canaryHelper' import * as canaryDeploymentHelper from './canaryHelper'
import * as podCanaryHelper from './podCanaryHelper'
import {isDeploymentEntity, isServiceEntity} from '../../types/kubernetesTypes' import {isDeploymentEntity, isServiceEntity} from '../../types/kubernetesTypes'
import {checkForErrors} from '../../utilities/kubectlUtils' import {checkForErrors} from '../../utilities/kubectlUtils'
import {inputAnnotations} from '../../inputUtils' import {inputAnnotations} from '../../inputUtils'
@@ -14,86 +13,67 @@ import {inputAnnotations} from '../../inputUtils'
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'
export async function deploySMICanary( export async function deploySMICanary(filePaths: string[], kubectl: Kubectl) {
filePaths: string[], const canaryReplicaCount = parseInt(
kubectl: Kubectl, core.getInput('baseline-and-canary-replicas')
onlyDeployStable: boolean = false )
) { if (canaryReplicaCount < 0 || canaryReplicaCount > 100)
const canaryReplicasInput = core.getInput('baseline-and-canary-replicas')
let canaryReplicaCount
let calculateReplicas = true
if (canaryReplicasInput !== '') {
canaryReplicaCount = parseInt(canaryReplicasInput)
calculateReplicas = false
core.debug(
`read replica count ${canaryReplicaCount} from input: ${canaryReplicasInput}`
)
}
if (canaryReplicaCount < 0 && canaryReplicaCount > 100)
throw Error('Baseline-and-canary-replicas must be between 0 and 100') throw Error('Baseline-and-canary-replicas must be between 0 and 100')
const newObjectsList = [] const newObjectsList = []
for await (const filePath of filePaths) { filePaths.forEach((filePath: string) => {
const fileContents = fs.readFileSync(filePath).toString() const fileContents = fs.readFileSync(filePath).toString()
const inputObjects = yaml.safeLoadAll(fileContents) yaml.safeLoadAll(fileContents, (inputObject) => {
for (const inputObject of inputObjects) {
const name = inputObject.metadata.name const name = inputObject.metadata.name
const kind = inputObject.kind const kind = inputObject.kind
if (!onlyDeployStable && isDeploymentEntity(kind)) { if (isDeploymentEntity(kind)) {
if (calculateReplicas) { const stableObject = canaryDeploymentHelper.fetchResource(
// calculate for each object
const percentage = parseInt(
core.getInput('percentage', {required: true})
)
canaryReplicaCount =
podCanaryHelper.calculateReplicaCountForCanary(
inputObject,
percentage
)
core.debug(`calculated replica count ${canaryReplicaCount}`)
}
core.debug('Creating canary object')
const newCanaryObject = canaryDeploymentHelper.getNewCanaryResource(
inputObject,
canaryReplicaCount
)
newObjectsList.push(newCanaryObject)
const stableObject = await canaryDeploymentHelper.fetchResource(
kubectl, kubectl,
kind, kind,
canaryDeploymentHelper.getStableResourceName(name) name
) )
if (stableObject) {
if (!stableObject) {
core.debug( core.debug(
`Stable object found for ${kind} ${name}. Creating baseline objects` 'Stable object not found. Creating only canary object'
) )
const newCanaryObject =
canaryDeploymentHelper.getNewCanaryResource(
inputObject,
canaryReplicaCount
)
newObjectsList.push(newCanaryObject)
} else {
if (
!canaryDeploymentHelper.isResourceMarkedAsStable(stableObject)
) {
throw Error(`StableSpecSelectorNotExist : ${name}`)
}
core.debug(
'Stable object found. Creating canary and baseline objects'
)
const newCanaryObject =
canaryDeploymentHelper.getNewCanaryResource(
inputObject,
canaryReplicaCount
)
const newBaselineObject = const newBaselineObject =
canaryDeploymentHelper.getBaselineDeploymentFromStableDeployment( canaryDeploymentHelper.getNewBaselineResource(
stableObject, stableObject,
canaryReplicaCount canaryReplicaCount
) )
newObjectsList.push(newCanaryObject)
newObjectsList.push(newBaselineObject) newObjectsList.push(newBaselineObject)
} }
} else if (isDeploymentEntity(kind)) {
core.debug(
`creating stable deployment with ${inputObject.spec.replicas} replicas`
)
const stableDeployment =
canaryDeploymentHelper.getStableResource(inputObject)
newObjectsList.push(stableDeployment)
} else { } else {
// Update non deployment entity or stable deployment as it is // Update non deployment entity as it is
newObjectsList.push(inputObject) newObjectsList.push(inputObject)
} }
} })
} })
core.debug(
`deploying canary objects with SMI: \n ${JSON.stringify(newObjectsList)}`
)
const newFilePaths = fileHelper.writeObjectsToFile(newObjectsList) const newFilePaths = fileHelper.writeObjectsToFile(newObjectsList)
const forceDeployment = core.getInput('force').toLowerCase() === 'true' const forceDeployment = core.getInput('force').toLowerCase() === 'true'
const result = await kubectl.apply(newFilePaths, forceDeployment) const result = await kubectl.apply(newFilePaths, forceDeployment)
@@ -103,7 +83,7 @@ export async function deploySMICanary(
async function createCanaryService(kubectl: Kubectl, filePaths: string[]) { async function createCanaryService(kubectl: Kubectl, filePaths: string[]) {
const newObjectsList = [] const newObjectsList = []
const trafficObjectsList: string[] = [] const trafficObjectsList = []
for (const filePath of filePaths) { for (const filePath of filePaths) {
const fileContents = fs.readFileSync(filePath).toString() const fileContents = fs.readFileSync(filePath).toString()
@@ -113,7 +93,6 @@ async function createCanaryService(kubectl: Kubectl, filePaths: string[]) {
const kind = inputObject.kind const kind = inputObject.kind
if (isServiceEntity(kind)) { if (isServiceEntity(kind)) {
core.debug(`Creating services for ${kind} ${name}`)
const newCanaryServiceObject = const newCanaryServiceObject =
canaryDeploymentHelper.getNewCanaryResource(inputObject) canaryDeploymentHelper.getNewCanaryResource(inputObject)
newObjectsList.push(newCanaryServiceObject) newObjectsList.push(newCanaryServiceObject)
@@ -176,7 +155,7 @@ async function createCanaryService(kubectl: Kubectl, filePaths: string[]) {
name name
) )
trafficObjectsList.push( trafficObjectsList.push(
await updateTrafficSplitObject(kubectl, name) updateTrafficSplitObject(kubectl, name)
) )
} }
} }
@@ -251,7 +230,7 @@ async function updateTrafficSplitObject(
kubectl: Kubectl, kubectl: Kubectl,
serviceName: string serviceName: string
): Promise<string> { ): Promise<string> {
const percentage = parseInt(core.getInput('percentage', {required: true})) const percentage = parseInt(core.getInput('percentage'))
if (percentage < 0 || percentage > 100) if (percentage < 0 || percentage > 100)
throw Error('Percentage must be between 0 and 100') throw Error('Percentage must be between 0 and 100')
@@ -262,9 +241,9 @@ async function updateTrafficSplitObject(
core.debug( core.debug(
'Creating the traffic object with canary weight: ' + 'Creating the traffic object with canary weight: ' +
baselineAndCanaryWeight + baselineAndCanaryWeight +
', baseline weight: ' + ',baseling weight: ' +
baselineAndCanaryWeight + baselineAndCanaryWeight +
', stable weight: ' + ',stable: ' +
stableDeploymentWeight stableDeploymentWeight
) )
return await createTrafficSplitManifestFile( return await createTrafficSplitManifestFile(
+8 -14
View File
@@ -3,7 +3,6 @@ import {createInlineArray} from '../utilities/arrayUtils'
import * as core from '@actions/core' import * as core from '@actions/core'
import * as toolCache from '@actions/tool-cache' import * as toolCache from '@actions/tool-cache'
import * as io from '@actions/io' import * as io from '@actions/io'
import {exec} from 'child_process'
export interface Resource { export interface Resource {
name: string name: string
@@ -143,16 +142,14 @@ export class Kubectl {
public async getResource( public async getResource(
resourceType: string, resourceType: string,
name: string, name: string
silentFailure: boolean = false
): Promise<ExecOutput> { ): Promise<ExecOutput> {
core.debug( return await this.execute([
'fetching resource of type ' + resourceType + ' and name ' + name 'get',
) `${resourceType}/${name}`,
return await this.execute( '-o',
['get', `${resourceType}/${name}`, '-o', 'json'], 'json'
silentFailure ])
)
} }
public executeCommand(command: string, args?: string) { public executeCommand(command: string, args?: string) {
@@ -173,10 +170,7 @@ export class Kubectl {
args = args.concat(['--namespace', this.namespace]) args = args.concat(['--namespace', this.namespace])
} }
core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`) core.debug(`Kubectl run with command: ${this.kubectlPath} ${args}`)
return await getExecOutput(this.kubectlPath, args, {silent})
return await getExecOutput(this.kubectlPath, args, {
silent
})
} }
} }