mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-06-25 14:09:27 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f742976337 |
@@ -74,9 +74,6 @@ Following are the key capabilities of this action:
|
|||||||
<td>traffic-split-method </br></br>(Optional)</td>
|
<td>traffic-split-method </br></br>(Optional)</td>
|
||||||
<td>Acceptable values: pod/smi.<br> Default value: pod <br>SMI: Percentage traffic split is done at request level using service mesh. Service mesh has to be setup by cluster admin. Orchestration of <a href="https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md" data-raw-source="TrafficSplit](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md)">TrafficSplit</a> objects of SMI is handled by this action. <br>Pod: Percentage split not possible at request level in the absence of service mesh. Percentage input is used to calculate the replicas for baseline and canary as a percentage of replicas specified in the input manifests for the stable variant.</td>
|
<td>Acceptable values: pod/smi.<br> Default value: pod <br>SMI: Percentage traffic split is done at request level using service mesh. Service mesh has to be setup by cluster admin. Orchestration of <a href="https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md" data-raw-source="TrafficSplit](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md)">TrafficSplit</a> objects of SMI is handled by this action. <br>Pod: Percentage split not possible at request level in the absence of service mesh. Percentage input is used to calculate the replicas for baseline and canary as a percentage of replicas specified in the input manifests for the stable variant.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>traffic-split-annotations </br></br>(Optional)</td>
|
|
||||||
<td>Annotations in the form of key/value pair to be added to TrafficSplit.</td>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>percentage </br></br>(Optional but required if strategy is canary)</td>
|
<td>percentage </br></br>(Optional but required if strategy is canary)</td>
|
||||||
<td>Used to compute the number of replicas of '-baseline' and '-canary' variants of the workloads found in manifest files. For the specified percentage input, if (percentage * numberOfDesirerdReplicas)/100 is not a round number, the floor of this number is used while creating '-baseline' and '-canary'.<br/><br/>For example, if Deployment hello-world was found in the input manifest file with 'replicas: 4' and if 'strategy: canary' and 'percentage: 25' are given as inputs to the action, then the Deployments hello-world-baseline and hello-world-canary are created with 1 replica each. The '-baseline' variant is created with the same image and tag as the stable version (4 replica variant prior to deployment) while the '-canary' variant is created with the image and tag corresponding to the new changes being deployed</td>
|
<td>Used to compute the number of replicas of '-baseline' and '-canary' variants of the workloads found in manifest files. For the specified percentage input, if (percentage * numberOfDesirerdReplicas)/100 is not a round number, the floor of this number is used while creating '-baseline' and '-canary'.<br/><br/>For example, if Deployment hello-world was found in the input manifest file with 'replicas: 4' and if 'strategy: canary' and 'percentage: 25' are given as inputs to the action, then the Deployments hello-world-baseline and hello-world-canary are created with 1 replica each. The '-baseline' variant is created with the same image and tag as the stable version (4 replica variant prior to deployment) while the '-canary' variant is created with the image and tag corresponding to the new changes being deployed</td>
|
||||||
|
|||||||
@@ -35,9 +35,6 @@ inputs:
|
|||||||
description: 'Traffic split method to be used. Allowed values are pod and smi'
|
description: 'Traffic split method to be used. Allowed values are pod and smi'
|
||||||
required: false
|
required: false
|
||||||
default: 'pod'
|
default: 'pod'
|
||||||
traffic-split-annotations:
|
|
||||||
description: 'Annotations in the form of key/value pair to be added to TrafficSplit. Relevant only if deployement strategy is blue-green or canary'
|
|
||||||
required: false
|
|
||||||
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
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ import {parseRouteStrategy} from '../types/routeStrategy'
|
|||||||
export async function deploy(
|
export async function deploy(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
manifestFilePaths: string[],
|
manifestFilePaths: string[],
|
||||||
deploymentStrategy: DeploymentStrategy,
|
deploymentStrategy: DeploymentStrategy
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
) {
|
) {
|
||||||
// update manifests
|
// update manifests
|
||||||
const inputManifestFiles: string[] = updateManifestFiles(manifestFilePaths)
|
const inputManifestFiles: string[] = updateManifestFiles(manifestFilePaths)
|
||||||
@@ -35,8 +34,7 @@ export async function deploy(
|
|||||||
inputManifestFiles,
|
inputManifestFiles,
|
||||||
deploymentStrategy,
|
deploymentStrategy,
|
||||||
kubectl,
|
kubectl,
|
||||||
trafficSplitMethod,
|
trafficSplitMethod
|
||||||
annotations
|
|
||||||
)
|
)
|
||||||
core.endGroup()
|
core.endGroup()
|
||||||
core.debug('Deployed manifest files: ' + deployedManifestFiles)
|
core.debug('Deployed manifest files: ' + deployedManifestFiles)
|
||||||
|
|||||||
+12
-26
@@ -40,15 +40,14 @@ import {parseRouteStrategy, RouteStrategy} from '../types/routeStrategy'
|
|||||||
export async function promote(
|
export async function promote(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
manifests: string[],
|
manifests: string[],
|
||||||
deploymentStrategy: DeploymentStrategy,
|
deploymentStrategy: DeploymentStrategy
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
) {
|
) {
|
||||||
switch (deploymentStrategy) {
|
switch (deploymentStrategy) {
|
||||||
case DeploymentStrategy.CANARY:
|
case DeploymentStrategy.CANARY:
|
||||||
await promoteCanary(kubectl, manifests)
|
await promoteCanary(kubectl, manifests)
|
||||||
break
|
break
|
||||||
case DeploymentStrategy.BLUE_GREEN:
|
case DeploymentStrategy.BLUE_GREEN:
|
||||||
await promoteBlueGreen(kubectl, manifests, annotations)
|
await promoteBlueGreen(kubectl, manifests)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
throw Error('Invalid promote deployment strategy')
|
throw Error('Invalid promote deployment strategy')
|
||||||
@@ -66,30 +65,26 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
|
|||||||
|
|
||||||
// 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.startGroup('Redirecting traffic to canary deployment')
|
core.info('Redirecting traffic to canary deployment')
|
||||||
await SMICanaryDeploymentHelper.redirectTrafficToCanaryDeployment(
|
await SMICanaryDeploymentHelper.redirectTrafficToCanaryDeployment(
|
||||||
kubectl,
|
kubectl,
|
||||||
manifests
|
manifests
|
||||||
)
|
)
|
||||||
core.endGroup()
|
|
||||||
|
|
||||||
core.startGroup('Deploying input manifests with SMI canary strategy')
|
core.info('Deploying input manifests with SMI canary strategy')
|
||||||
await deploy.deploy(kubectl, manifests, DeploymentStrategy.CANARY)
|
await deploy.deploy(kubectl, manifests, DeploymentStrategy.CANARY)
|
||||||
core.endGroup()
|
|
||||||
|
|
||||||
core.startGroup('Redirecting traffic to stable deployment')
|
core.info('Redirecting traffic to stable deployment')
|
||||||
await SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(
|
await SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(
|
||||||
kubectl,
|
kubectl,
|
||||||
manifests
|
manifests
|
||||||
)
|
)
|
||||||
core.endGroup()
|
|
||||||
} else {
|
} else {
|
||||||
core.startGroup('Deploying input manifests')
|
core.info('Deploying input manifests')
|
||||||
await deploy.deploy(kubectl, manifests, DeploymentStrategy.CANARY)
|
await deploy.deploy(kubectl, manifests, DeploymentStrategy.CANARY)
|
||||||
core.endGroup()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
core.startGroup('Deleting canary and baseline workloads')
|
core.info('Deleting canary and baseline workloads')
|
||||||
try {
|
try {
|
||||||
await canaryDeploymentHelper.deleteCanaryDeployment(
|
await canaryDeploymentHelper.deleteCanaryDeployment(
|
||||||
kubectl,
|
kubectl,
|
||||||
@@ -102,14 +97,9 @@ async function promoteCanary(kubectl: Kubectl, manifests: string[]) {
|
|||||||
ex
|
ex
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
core.endGroup()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function promoteBlueGreen(
|
async function promoteBlueGreen(kubectl: Kubectl, manifests: string[]) {
|
||||||
kubectl: Kubectl,
|
|
||||||
manifests: string[],
|
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
) {
|
|
||||||
// update container images and pull secrets
|
// update container images and pull secrets
|
||||||
const inputManifestFiles: string[] = updateManifestFiles(manifests)
|
const inputManifestFiles: string[] = updateManifestFiles(manifests)
|
||||||
const manifestObjects: BlueGreenManifests =
|
const manifestObjects: BlueGreenManifests =
|
||||||
@@ -119,7 +109,7 @@ async function promoteBlueGreen(
|
|||||||
core.getInput('route-method', {required: true})
|
core.getInput('route-method', {required: true})
|
||||||
)
|
)
|
||||||
|
|
||||||
core.startGroup('Deleting old deployment and making new one')
|
core.info('Deleting old deployment and making new one')
|
||||||
let result
|
let result
|
||||||
if (routeStrategy == RouteStrategy.INGRESS) {
|
if (routeStrategy == RouteStrategy.INGRESS) {
|
||||||
result = await promoteBlueGreenIngress(kubectl, manifestObjects)
|
result = await promoteBlueGreenIngress(kubectl, manifestObjects)
|
||||||
@@ -128,10 +118,9 @@ async function promoteBlueGreen(
|
|||||||
} else {
|
} else {
|
||||||
result = await promoteBlueGreenService(kubectl, manifestObjects)
|
result = await promoteBlueGreenService(kubectl, manifestObjects)
|
||||||
}
|
}
|
||||||
core.endGroup()
|
|
||||||
|
|
||||||
// checking stability of newly created deployments
|
// checking stability of newly created deployments
|
||||||
core.startGroup('Checking manifest stability')
|
core.info('Checking manifest stability')
|
||||||
const deployedManifestFiles = result.newFilePaths
|
const deployedManifestFiles = result.newFilePaths
|
||||||
const resources: Resource[] = getResources(
|
const resources: Resource[] = getResources(
|
||||||
deployedManifestFiles,
|
deployedManifestFiles,
|
||||||
@@ -140,9 +129,8 @@ async function promoteBlueGreen(
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
await KubernetesManifestUtility.checkManifestStability(kubectl, resources)
|
await KubernetesManifestUtility.checkManifestStability(kubectl, resources)
|
||||||
core.endGroup()
|
|
||||||
|
|
||||||
core.startGroup(
|
core.info(
|
||||||
'Routing to new deployments and deleting old workloads and services'
|
'Routing to new deployments and deleting old workloads and services'
|
||||||
)
|
)
|
||||||
if (routeStrategy == RouteStrategy.INGRESS) {
|
if (routeStrategy == RouteStrategy.INGRESS) {
|
||||||
@@ -162,8 +150,7 @@ async function promoteBlueGreen(
|
|||||||
await routeBlueGreenSMI(
|
await routeBlueGreenSMI(
|
||||||
kubectl,
|
kubectl,
|
||||||
NONE_LABEL_VALUE,
|
NONE_LABEL_VALUE,
|
||||||
manifestObjects.serviceEntityList,
|
manifestObjects.serviceEntityList
|
||||||
annotations
|
|
||||||
)
|
)
|
||||||
await deleteWorkloadsWithLabel(
|
await deleteWorkloadsWithLabel(
|
||||||
kubectl,
|
kubectl,
|
||||||
@@ -183,5 +170,4 @@ async function promoteBlueGreen(
|
|||||||
manifestObjects.deploymentEntityList
|
manifestObjects.deploymentEntityList
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
core.endGroup()
|
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-15
@@ -15,15 +15,14 @@ import {parseRouteStrategy, RouteStrategy} from '../types/routeStrategy'
|
|||||||
export async function reject(
|
export async function reject(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
manifests: string[],
|
manifests: string[],
|
||||||
deploymentStrategy: DeploymentStrategy,
|
deploymentStrategy: DeploymentStrategy
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
) {
|
) {
|
||||||
switch (deploymentStrategy) {
|
switch (deploymentStrategy) {
|
||||||
case DeploymentStrategy.CANARY:
|
case DeploymentStrategy.CANARY:
|
||||||
await rejectCanary(kubectl, manifests)
|
await rejectCanary(kubectl, manifests)
|
||||||
break
|
break
|
||||||
case DeploymentStrategy.BLUE_GREEN:
|
case DeploymentStrategy.BLUE_GREEN:
|
||||||
await rejectBlueGreen(kubectl, manifests, annotations)
|
await rejectBlueGreen(kubectl, manifests)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
throw 'Invalid delete deployment strategy'
|
throw 'Invalid delete deployment strategy'
|
||||||
@@ -37,30 +36,24 @@ async function rejectCanary(kubectl: Kubectl, manifests: string[]) {
|
|||||||
core.getInput('traffic-split-method', {required: true})
|
core.getInput('traffic-split-method', {required: true})
|
||||||
)
|
)
|
||||||
if (trafficSplitMethod == TrafficSplitMethod.SMI) {
|
if (trafficSplitMethod == TrafficSplitMethod.SMI) {
|
||||||
core.startGroup('Rejecting deployment with SMI canary strategy')
|
core.info('Rejecting deployment with SMI canary strategy')
|
||||||
includeServices = true
|
includeServices = true
|
||||||
await SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(
|
await SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(
|
||||||
kubectl,
|
kubectl,
|
||||||
manifests
|
manifests
|
||||||
)
|
)
|
||||||
core.endGroup()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
core.startGroup('Deleting baseline and canary workloads')
|
core.info('Deleting baseline and canary workloads')
|
||||||
await canaryDeploymentHelper.deleteCanaryDeployment(
|
await canaryDeploymentHelper.deleteCanaryDeployment(
|
||||||
kubectl,
|
kubectl,
|
||||||
manifests,
|
manifests,
|
||||||
includeServices
|
includeServices
|
||||||
)
|
)
|
||||||
core.endGroup()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function rejectBlueGreen(
|
async function rejectBlueGreen(kubectl: Kubectl, manifests: string[]) {
|
||||||
kubectl: Kubectl,
|
core.info('Rejecting deployment with blue green strategy')
|
||||||
manifests: string[],
|
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
) {
|
|
||||||
core.startGroup('Rejecting deployment with blue green strategy')
|
|
||||||
|
|
||||||
const routeStrategy = parseRouteStrategy(
|
const routeStrategy = parseRouteStrategy(
|
||||||
core.getInput('route-method', {required: true})
|
core.getInput('route-method', {required: true})
|
||||||
@@ -68,9 +61,8 @@ async function rejectBlueGreen(
|
|||||||
if (routeStrategy == RouteStrategy.INGRESS) {
|
if (routeStrategy == RouteStrategy.INGRESS) {
|
||||||
await rejectBlueGreenIngress(kubectl, manifests)
|
await rejectBlueGreenIngress(kubectl, manifests)
|
||||||
} else if (routeStrategy == RouteStrategy.SMI) {
|
} else if (routeStrategy == RouteStrategy.SMI) {
|
||||||
await rejectBlueGreenSMI(kubectl, manifests, annotations)
|
await rejectBlueGreenSMI(kubectl, manifests)
|
||||||
} else {
|
} else {
|
||||||
await rejectBlueGreenService(kubectl, manifests)
|
await rejectBlueGreenService(kubectl, manifests)
|
||||||
}
|
}
|
||||||
core.endGroup()
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-7
@@ -6,7 +6,6 @@ import {reject} from './actions/reject'
|
|||||||
import {Action, parseAction} from './types/action'
|
import {Action, parseAction} from './types/action'
|
||||||
import {parseDeploymentStrategy} from './types/deploymentStrategy'
|
import {parseDeploymentStrategy} from './types/deploymentStrategy'
|
||||||
import {getFilesFromDirectories} from './utilities/fileUtils'
|
import {getFilesFromDirectories} from './utilities/fileUtils'
|
||||||
import {parseAnnotations} from './types/annotations'
|
|
||||||
|
|
||||||
export async function run() {
|
export async function run() {
|
||||||
// verify kubeconfig is set
|
// verify kubeconfig is set
|
||||||
@@ -19,9 +18,6 @@ export async function run() {
|
|||||||
const action: Action | undefined = parseAction(
|
const action: Action | undefined = parseAction(
|
||||||
core.getInput('action', {required: true})
|
core.getInput('action', {required: true})
|
||||||
)
|
)
|
||||||
const annotations = parseAnnotations(
|
|
||||||
core.getInput('annotations', {required: false})
|
|
||||||
)
|
|
||||||
const strategy = parseDeploymentStrategy(core.getInput('strategy'))
|
const strategy = parseDeploymentStrategy(core.getInput('strategy'))
|
||||||
const manifestsInput = core.getInput('manifests', {required: true})
|
const manifestsInput = core.getInput('manifests', {required: true})
|
||||||
const manifestFilePaths = manifestsInput
|
const manifestFilePaths = manifestsInput
|
||||||
@@ -38,15 +34,15 @@ export async function run() {
|
|||||||
// run action
|
// run action
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case Action.DEPLOY: {
|
case Action.DEPLOY: {
|
||||||
await deploy(kubectl, fullManifestFilePaths, strategy, annotations)
|
await deploy(kubectl, fullManifestFilePaths, strategy)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case Action.PROMOTE: {
|
case Action.PROMOTE: {
|
||||||
await promote(kubectl, fullManifestFilePaths, strategy, annotations)
|
await promote(kubectl, fullManifestFilePaths, strategy)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case Action.REJECT: {
|
case Action.REJECT: {
|
||||||
await reject(kubectl, fullManifestFilePaths, strategy, annotations)
|
await reject(kubectl, fullManifestFilePaths, strategy)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ export interface BlueGreenManifests {
|
|||||||
export async function routeBlueGreen(
|
export async function routeBlueGreen(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
inputManifestFiles: string[],
|
inputManifestFiles: string[],
|
||||||
routeStrategy: RouteStrategy,
|
routeStrategy: RouteStrategy
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
) {
|
) {
|
||||||
// sleep for buffer time
|
// sleep for buffer time
|
||||||
const bufferTime: number = parseInt(
|
const bufferTime: number = parseInt(
|
||||||
@@ -75,8 +74,7 @@ export async function routeBlueGreen(
|
|||||||
await routeBlueGreenSMI(
|
await routeBlueGreenSMI(
|
||||||
kubectl,
|
kubectl,
|
||||||
GREEN_LABEL_VALUE,
|
GREEN_LABEL_VALUE,
|
||||||
manifestObjects.serviceEntityList,
|
manifestObjects.serviceEntityList
|
||||||
annotations
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
await routeBlueGreenService(
|
await routeBlueGreenService(
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ const MAX_VAL = 100
|
|||||||
|
|
||||||
export async function deployBlueGreenSMI(
|
export async function deployBlueGreenSMI(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
filePaths: string[],
|
filePaths: string[]
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
) {
|
) {
|
||||||
// get all kubernetes objects defined in manifest files
|
// get all kubernetes objects defined in manifest files
|
||||||
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths)
|
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths)
|
||||||
@@ -38,7 +37,7 @@ export async function deployBlueGreenSMI(
|
|||||||
await kubectl.apply(manifestFiles)
|
await kubectl.apply(manifestFiles)
|
||||||
|
|
||||||
// make extraservices and trafficsplit
|
// make extraservices and trafficsplit
|
||||||
await setupSMI(kubectl, manifestObjects.serviceEntityList, annotations)
|
await setupSMI(kubectl, manifestObjects.serviceEntityList)
|
||||||
|
|
||||||
// create new deloyments
|
// create new deloyments
|
||||||
return await createWorkloadsWithLabel(
|
return await createWorkloadsWithLabel(
|
||||||
@@ -69,18 +68,16 @@ export async function promoteBlueGreenSMI(kubectl: Kubectl, manifestObjects) {
|
|||||||
|
|
||||||
export async function rejectBlueGreenSMI(
|
export async function rejectBlueGreenSMI(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
filePaths: string[],
|
filePaths: string[]
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
) {
|
) {
|
||||||
// get all kubernetes objects defined in manifest files
|
// get all kubernetes objects defined in manifest files
|
||||||
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths)
|
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths)
|
||||||
|
|
||||||
// route trafficsplit to stable deployments
|
// route trafficsplit to stable deploymetns
|
||||||
await routeBlueGreenSMI(
|
await routeBlueGreenSMI(
|
||||||
kubectl,
|
kubectl,
|
||||||
NONE_LABEL_VALUE,
|
NONE_LABEL_VALUE,
|
||||||
manifestObjects.serviceEntityList,
|
manifestObjects.serviceEntityList
|
||||||
annotations
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// delete rejected new bluegreen deployments
|
// delete rejected new bluegreen deployments
|
||||||
@@ -94,11 +91,7 @@ export async function rejectBlueGreenSMI(
|
|||||||
await cleanupSMI(kubectl, manifestObjects.serviceEntityList)
|
await cleanupSMI(kubectl, manifestObjects.serviceEntityList)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setupSMI(
|
export async function setupSMI(kubectl: Kubectl, serviceEntityList: any[]) {
|
||||||
kubectl: Kubectl,
|
|
||||||
serviceEntityList: any[],
|
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
) {
|
|
||||||
const newObjectsList = []
|
const newObjectsList = []
|
||||||
const trafficObjectList = []
|
const trafficObjectList = []
|
||||||
|
|
||||||
@@ -124,8 +117,7 @@ export async function setupSMI(
|
|||||||
createTrafficSplitObject(
|
createTrafficSplitObject(
|
||||||
kubectl,
|
kubectl,
|
||||||
inputObject.metadata.name,
|
inputObject.metadata.name,
|
||||||
NONE_LABEL_VALUE,
|
NONE_LABEL_VALUE
|
||||||
annotations
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -135,8 +127,7 @@ let trafficSplitAPIVersion = ''
|
|||||||
async function createTrafficSplitObject(
|
async function createTrafficSplitObject(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
name: string,
|
name: string,
|
||||||
nextLabel: string,
|
nextLabel: string
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
// cache traffic split api version
|
// cache traffic split api version
|
||||||
if (!trafficSplitAPIVersion)
|
if (!trafficSplitAPIVersion)
|
||||||
@@ -154,8 +145,7 @@ async function createTrafficSplitObject(
|
|||||||
apiVersion: trafficSplitAPIVersion,
|
apiVersion: trafficSplitAPIVersion,
|
||||||
kind: 'TrafficSplit',
|
kind: 'TrafficSplit',
|
||||||
metadata: {
|
metadata: {
|
||||||
name: getBlueGreenResourceName(name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX),
|
name: getBlueGreenResourceName(name, TRAFFIC_SPLIT_OBJECT_NAME_SUFFIX)
|
||||||
annotations: annotations
|
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
service: name,
|
service: name,
|
||||||
@@ -204,16 +194,14 @@ export function getSMIServiceResource(
|
|||||||
export async function routeBlueGreenSMI(
|
export async function routeBlueGreenSMI(
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
nextLabel: string,
|
nextLabel: string,
|
||||||
serviceEntityList: any[],
|
serviceEntityList: any[]
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
) {
|
) {
|
||||||
for (const serviceObject of serviceEntityList) {
|
for (const serviceObject of serviceEntityList) {
|
||||||
// route trafficsplit to given label
|
// route trafficsplit to given label
|
||||||
await createTrafficSplitObject(
|
await createTrafficSplitObject(
|
||||||
kubectl,
|
kubectl,
|
||||||
serviceObject.metadata.name,
|
serviceObject.metadata.name,
|
||||||
nextLabel,
|
nextLabel
|
||||||
annotations
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,8 +288,7 @@ async function getTrafficSplitObject(
|
|||||||
name: string,
|
name: string,
|
||||||
stableWeight: number,
|
stableWeight: number,
|
||||||
baselineWeight: number,
|
baselineWeight: number,
|
||||||
canaryWeight: number,
|
canaryWeight: number
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
// cached version
|
// cached version
|
||||||
if (!trafficSplitAPIVersion) {
|
if (!trafficSplitAPIVersion) {
|
||||||
@@ -302,8 +301,7 @@ async function getTrafficSplitObject(
|
|||||||
apiVersion: trafficSplitAPIVersion,
|
apiVersion: trafficSplitAPIVersion,
|
||||||
kind: 'TrafficSplit',
|
kind: 'TrafficSplit',
|
||||||
metadata: {
|
metadata: {
|
||||||
name: getTrafficSplitResourceName(name),
|
name: getTrafficSplitResourceName(name)
|
||||||
annotations: annotations
|
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
backends: [
|
backends: [
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ import {parseRouteStrategy, RouteStrategy} from '../types/routeStrategy'
|
|||||||
import {ExecOutput} from '@actions/exec'
|
import {ExecOutput} from '@actions/exec'
|
||||||
import {
|
import {
|
||||||
getWorkflowAnnotationKeyLabel,
|
getWorkflowAnnotationKeyLabel,
|
||||||
getWorkflowAnnotations,
|
getWorkflowAnnotations
|
||||||
cleanLabel
|
|
||||||
} from '../utilities/workflowAnnotationUtils'
|
} from '../utilities/workflowAnnotationUtils'
|
||||||
import {
|
import {
|
||||||
annotateChildPods,
|
annotateChildPods,
|
||||||
@@ -41,8 +40,7 @@ export async function deployManifests(
|
|||||||
files: string[],
|
files: string[],
|
||||||
deploymentStrategy: DeploymentStrategy,
|
deploymentStrategy: DeploymentStrategy,
|
||||||
kubectl: Kubectl,
|
kubectl: Kubectl,
|
||||||
trafficSplitMethod: TrafficSplitMethod,
|
trafficSplitMethod: TrafficSplitMethod
|
||||||
annotations: {[key: string]: string} = {}
|
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
switch (deploymentStrategy) {
|
switch (deploymentStrategy) {
|
||||||
case DeploymentStrategy.CANARY: {
|
case DeploymentStrategy.CANARY: {
|
||||||
@@ -64,7 +62,7 @@ export async function deployManifests(
|
|||||||
(routeStrategy == RouteStrategy.INGRESS &&
|
(routeStrategy == RouteStrategy.INGRESS &&
|
||||||
deployBlueGreenIngress(kubectl, files)) ||
|
deployBlueGreenIngress(kubectl, files)) ||
|
||||||
(routeStrategy == RouteStrategy.SMI &&
|
(routeStrategy == RouteStrategy.SMI &&
|
||||||
deployBlueGreenSMI(kubectl, files, annotations)) ||
|
deployBlueGreenSMI(kubectl, files)) ||
|
||||||
deployBlueGreenService(kubectl, files)
|
deployBlueGreenService(kubectl, files)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -216,10 +214,10 @@ async function labelResources(
|
|||||||
label: string
|
label: string
|
||||||
) {
|
) {
|
||||||
const labels = [
|
const labels = [
|
||||||
`workflowFriendlyName=${cleanLabel(
|
`workflowFriendlyName=${normalizeWorkflowStrLabel(
|
||||||
normalizeWorkflowStrLabel(process.env.GITHUB_WORKFLOW)
|
process.env.GITHUB_WORKFLOW
|
||||||
)}`,
|
)}`,
|
||||||
`workflow=${cleanLabel(label)}`
|
`workflow=${label}`
|
||||||
]
|
]
|
||||||
|
|
||||||
checkForErrors([await kubectl.labelFiles(files, labels)], true)
|
checkForErrors([await kubectl.labelFiles(files, labels)], true)
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
export function parseAnnotations(str: string) {
|
|
||||||
if (str == '') {
|
|
||||||
return {}
|
|
||||||
} else {
|
|
||||||
const annotaion = JSON.parse(str)
|
|
||||||
return new Map(annotaion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
import {
|
import {prefixObjectKeys} from '../utilities/workflowAnnotationUtils'
|
||||||
cleanLabel,
|
|
||||||
prefixObjectKeys
|
|
||||||
} from '../utilities/workflowAnnotationUtils'
|
|
||||||
|
|
||||||
describe('WorkflowAnnotationUtils', () => {
|
describe('WorkflowAnnotationUtils', () => {
|
||||||
describe('prefixObjectKeys', () => {
|
describe('prefixObjectKeys', () => {
|
||||||
@@ -18,16 +15,4 @@ describe('WorkflowAnnotationUtils', () => {
|
|||||||
expect(prefixObjectKeys(obj, prefix)).toEqual(expected)
|
expect(prefixObjectKeys(obj, prefix)).toEqual(expected)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('cleanLabel', () => {
|
|
||||||
it('should clean label', () => {
|
|
||||||
const alreadyClean = 'alreadyClean'
|
|
||||||
expect(cleanLabel(alreadyClean)).toEqual(alreadyClean)
|
|
||||||
expect(cleanLabel('.startInvalid')).toEqual('startInvalid')
|
|
||||||
expect(cleanLabel('with%S0ME&invalid#chars')).toEqual(
|
|
||||||
'withS0MEinvalidchars'
|
|
||||||
)
|
|
||||||
expect(cleanLabel('with⚒️emoji')).toEqual('withemoji')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -47,14 +47,3 @@ export function getWorkflowAnnotationKeyLabel(
|
|||||||
.digest('hex')
|
.digest('hex')
|
||||||
return `githubWorkflow_${hashKey}`
|
return `githubWorkflow_${hashKey}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleans label to match valid kubernetes label specification by removing invalid characters
|
|
||||||
* @param label
|
|
||||||
* @returns cleaned label
|
|
||||||
*/
|
|
||||||
export function cleanLabel(label: string): string {
|
|
||||||
const removedInvalidChars = label.replace(/[^-A-Za-z0-9_.]/gi, '')
|
|
||||||
const regex = /([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]/
|
|
||||||
return regex.exec(removedInvalidChars)[0] || ''
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user