mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-04-13 10:02:19 +08:00
* Add missing API switch for GHES (#200) * Vidya reddy/prettier code (#203) * switch none deployment strategy to basic (#204) * switch none deployment strategy to basic * update readme * update deployment strategy fallthrough logic * comment fixed * add disclaimer for basic strategy only supporting deploy action * Hari/beautify logs (#206) * Logging changes for deploy * Logging Changes with group * format check changes * Add ncc build to build script (#208) Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com> * Logging Changes for Promote, Reject actions (#207) * add clean function (#211) * Added Traffic split annotations (#215) * Added Traffic split annotations * traffic split - blueGreen deployment * traffic split - canary deployment * Traffic split annotations - canary deployment * updated Readme and action.yml * Traffic split - canary deployment * clean code * Clean code * Clean code * Create annotation object * Updated Readme and action.yml * Spelling correction Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com> * Swap annotation key to actions.github.com prefix (#216) * Private Cluster functionality (#214) * Fixed Blue/Green Strategy Ingress Route-Method Glitch (#217) * Added some tests, not sure what else to try but gonna think of more examples * forgot some files * reverted package-lock.json * Added empty dir test * Cleaned up some extra spaces * Add node modules and compiled JavaScript from main * forgot to actually include functionality * removed unnecessary files * Update .gitignore * Update .gitignore * Update .gitignore * thx david * renamed searchFilesRec * integrations test fix * added examples to README * added note about depth * added additional note * removed ticks * changed version string * removed conflict on readme * Added tests for bluegreen helper and resolved issue with ingress not being read correctly, still have to figure out why new services aren't showing up * resolved services name issue * looks functional, beginning refactor now * refactored deploy methods for type error * Removed refactor comments * prettier * implemented Oliver's feedback * prettier * added optional chaining operator * removed refactor comment Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MacBook-Pro.local> Co-authored-by: Oliver King <oking3@uncc.edu> Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan> * Add node modules and compiled JavaScript from main Co-authored-by: nv35 <76777923+nv35@users.noreply.github.com> Co-authored-by: Vidya <59590642+Vidya2606@users.noreply.github.com> Co-authored-by: David Gamero <david340804@gmail.com> Co-authored-by: Hariharan Subramanian <105889062+hsubramanianaks@users.noreply.github.com> Co-authored-by: Vidya Reddy <vidyareddy@microsoft.com> Co-authored-by: Oliver King <oking3@uncc.edu> Co-authored-by: Marcus-Hines <marcus.chris.hines@gmail.com> Co-authored-by: Jaiveer Katariya <35347859+jaiveerk@users.noreply.github.com> Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MacBook-Pro.local> Co-authored-by: Jaiveer Katariya <jaiveerkatariya@Jaiveers-MBP.lan>
253 lines
7.0 KiB
TypeScript
253 lines
7.0 KiB
TypeScript
import {Kubectl} from '../../types/kubectl'
|
|
import * as fileHelper from '../../utilities/fileUtils'
|
|
import {
|
|
addBlueGreenLabelsAndAnnotations,
|
|
BLUE_GREEN_VERSION_LABEL,
|
|
BlueGreenManifests,
|
|
createWorkloadsWithLabel,
|
|
deleteWorkloadsAndServicesWithLabel,
|
|
fetchResource,
|
|
getManifestObjects,
|
|
getNewBlueGreenObject,
|
|
GREEN_LABEL_VALUE,
|
|
NONE_LABEL_VALUE
|
|
} from './blueGreenHelper'
|
|
import * as core from '@actions/core'
|
|
|
|
const BACKEND = 'backend'
|
|
|
|
export async function deployBlueGreenIngress(
|
|
kubectl: Kubectl,
|
|
filePaths: string[]
|
|
) {
|
|
// get all kubernetes objects defined in manifest files
|
|
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths)
|
|
|
|
// create deployments with green label value
|
|
const workloadDeployment = await createWorkloadsWithLabel(
|
|
kubectl,
|
|
manifestObjects.deploymentEntityList,
|
|
GREEN_LABEL_VALUE
|
|
)
|
|
|
|
let newObjectsList = []
|
|
manifestObjects.serviceEntityList.forEach((inputObject) => {
|
|
const newBlueGreenObject = getNewBlueGreenObject(
|
|
inputObject,
|
|
GREEN_LABEL_VALUE
|
|
)
|
|
newObjectsList.push(newBlueGreenObject)
|
|
})
|
|
newObjectsList = newObjectsList
|
|
.concat(manifestObjects.otherObjects)
|
|
.concat(manifestObjects.unroutedServiceEntityList)
|
|
|
|
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
|
await kubectl.apply(manifestFiles)
|
|
|
|
core.debug(
|
|
'new objects after processing services and other objects: \n' +
|
|
JSON.stringify(newObjectsList)
|
|
)
|
|
|
|
return {workloadDeployment, newObjectsList}
|
|
}
|
|
|
|
export async function promoteBlueGreenIngress(
|
|
kubectl: Kubectl,
|
|
manifestObjects
|
|
) {
|
|
//checking if anything to promote
|
|
const {areValid, invalidIngresses} = validateIngresses(
|
|
kubectl,
|
|
manifestObjects.ingressEntityList,
|
|
manifestObjects.serviceNameMap
|
|
)
|
|
if (!areValid) {
|
|
throw 'Ingresses are not in promote state' + invalidIngresses.toString()
|
|
}
|
|
|
|
// create stable deployments with new configuration
|
|
const result = createWorkloadsWithLabel(
|
|
kubectl,
|
|
manifestObjects.deploymentEntityList,
|
|
NONE_LABEL_VALUE
|
|
)
|
|
|
|
// create stable services with new configuration
|
|
const newObjectsList = []
|
|
manifestObjects.serviceEntityList.forEach((inputObject) => {
|
|
const newBlueGreenObject = getNewBlueGreenObject(
|
|
inputObject,
|
|
NONE_LABEL_VALUE
|
|
)
|
|
newObjectsList.push(newBlueGreenObject)
|
|
})
|
|
|
|
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
|
await kubectl.apply(manifestFiles)
|
|
|
|
return result
|
|
}
|
|
|
|
export async function rejectBlueGreenIngress(
|
|
kubectl: Kubectl,
|
|
filePaths: string[]
|
|
) {
|
|
// get all kubernetes objects defined in manifest files
|
|
const manifestObjects: BlueGreenManifests = getManifestObjects(filePaths)
|
|
|
|
// route ingress to stables services
|
|
await routeBlueGreenIngress(
|
|
kubectl,
|
|
null,
|
|
manifestObjects.serviceNameMap,
|
|
manifestObjects.ingressEntityList
|
|
)
|
|
|
|
// delete green services and deployments
|
|
await deleteWorkloadsAndServicesWithLabel(
|
|
kubectl,
|
|
GREEN_LABEL_VALUE,
|
|
manifestObjects.deploymentEntityList,
|
|
manifestObjects.serviceEntityList
|
|
)
|
|
}
|
|
|
|
export async function routeBlueGreenIngress(
|
|
kubectl: Kubectl,
|
|
nextLabel: string,
|
|
serviceNameMap: Map<string, string>,
|
|
ingressEntityList: any[]
|
|
) {
|
|
let newObjectsList = []
|
|
|
|
if (!nextLabel) {
|
|
newObjectsList = ingressEntityList.filter((ingress) =>
|
|
isIngressRouted(ingress, serviceNameMap)
|
|
)
|
|
} else {
|
|
ingressEntityList.forEach((inputObject) => {
|
|
if (isIngressRouted(inputObject, serviceNameMap)) {
|
|
const newBlueGreenIngressObject = getUpdatedBlueGreenIngress(
|
|
inputObject,
|
|
serviceNameMap,
|
|
GREEN_LABEL_VALUE
|
|
)
|
|
newObjectsList.push(newBlueGreenIngressObject)
|
|
} else {
|
|
newObjectsList.push(inputObject)
|
|
}
|
|
})
|
|
}
|
|
|
|
const manifestFiles = fileHelper.writeObjectsToFile(newObjectsList)
|
|
await kubectl.apply(manifestFiles)
|
|
return newObjectsList
|
|
}
|
|
|
|
export function validateIngresses(
|
|
kubectl: Kubectl,
|
|
ingressEntityList: any[],
|
|
serviceNameMap: Map<string, string>
|
|
): {areValid: boolean; invalidIngresses: string[]} {
|
|
let areValid: boolean = true
|
|
const invalidIngresses = []
|
|
ingressEntityList.forEach(async (inputObject) => {
|
|
if (isIngressRouted(inputObject, serviceNameMap)) {
|
|
//querying existing ingress
|
|
const existingIngress = await fetchResource(
|
|
kubectl,
|
|
inputObject.kind,
|
|
inputObject.metadata.name
|
|
)
|
|
|
|
let isValid =
|
|
!!existingIngress &&
|
|
existingIngress?.metadata?.labels[BLUE_GREEN_VERSION_LABEL] ===
|
|
GREEN_LABEL_VALUE
|
|
if (!isValid) {
|
|
invalidIngresses.push(inputObject.metadata.name)
|
|
}
|
|
// to be valid, ingress should exist and should be green
|
|
areValid = areValid && isValid
|
|
}
|
|
})
|
|
|
|
return {areValid, invalidIngresses}
|
|
}
|
|
|
|
export function isIngressRouted(
|
|
ingressObject: any,
|
|
serviceNameMap: Map<string, string>
|
|
): boolean {
|
|
let isIngressRouted: boolean = false
|
|
// check if ingress targets a service in the given manifests
|
|
JSON.parse(JSON.stringify(ingressObject), (key, value) => {
|
|
isIngressRouted =
|
|
isIngressRouted || (key === 'service' && value.hasOwnProperty('name'))
|
|
isIngressRouted =
|
|
isIngressRouted || (key === 'serviceName' && serviceNameMap.has(value))
|
|
|
|
return value
|
|
})
|
|
|
|
return isIngressRouted
|
|
}
|
|
|
|
export function getUpdatedBlueGreenIngress(
|
|
inputObject: any,
|
|
serviceNameMap: Map<string, string>,
|
|
type: string
|
|
): object {
|
|
if (!type) {
|
|
return inputObject
|
|
}
|
|
|
|
const newObject = JSON.parse(JSON.stringify(inputObject))
|
|
// add green labels and values
|
|
addBlueGreenLabelsAndAnnotations(newObject, type)
|
|
|
|
// update ingress labels
|
|
if (inputObject.apiVersion === 'networking.k8s.io/v1beta1') {
|
|
return updateIngressBackendBetaV1(newObject, serviceNameMap)
|
|
}
|
|
return updateIngressBackend(newObject, serviceNameMap)
|
|
}
|
|
|
|
export function updateIngressBackendBetaV1(
|
|
inputObject: any,
|
|
serviceNameMap: Map<string, string>
|
|
): any {
|
|
inputObject = JSON.parse(JSON.stringify(inputObject), (key, value) => {
|
|
if (key.toLowerCase() === BACKEND) {
|
|
const {serviceName} = value
|
|
if (serviceNameMap.has(serviceName)) {
|
|
// update service name with corresponding bluegreen name only if service is provied in given manifests
|
|
value.serviceName = serviceNameMap.get(serviceName)
|
|
}
|
|
}
|
|
|
|
return value
|
|
})
|
|
|
|
return inputObject
|
|
}
|
|
|
|
export function updateIngressBackend(
|
|
inputObject: any,
|
|
serviceNameMap: Map<string, string>
|
|
): any {
|
|
inputObject = JSON.parse(JSON.stringify(inputObject), (key, value) => {
|
|
if (
|
|
key.toLowerCase() === BACKEND &&
|
|
serviceNameMap.has(value?.service?.name)
|
|
) {
|
|
value.service.name = serviceNameMap.get(value.service.name)
|
|
}
|
|
return value
|
|
})
|
|
|
|
return inputObject
|
|
}
|