mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-04-23 00:12:17 +08:00
* Migrate build toolchain from ncc/Jest to esbuild/Vitest Replace the legacy ncc/Jest/Babel build stack with a modern ESM toolchain: Build: - Replace @vercel/ncc with esbuild (--platform=node --target=node20 --format=esm) - Add createRequire banner for CJS interop in ESM bundle - Add "type": "module" to package.json - Add tsc --noEmit typecheck script (esbuild strips types without checking) - Add typecheck to husky pre-commit hook Dependencies: - Bump @actions/core@3, exec@3, io@3, tool-cache@4 (ESM-only) - Replace jest/ts-jest/@babel/* with vitest@4 Tests: - Convert 29 test files: jest.fn()→vi.fn(), jest.mock()→vi.mock(), jest.spyOn()→vi.spyOn() - Fix vitest 4 compat: mockImplementation requires args, mock call tracking, await .rejects CI: - Update build step from ncc build → npm run build - Update composite action to use npm run build * Switch tsconfig to NodeNext module resolution Change module/moduleResolution from ES2022/bundler to NodeNext/NodeNext and target from ES2022 to ES2020. - Add .js extensions to all relative imports across 59 source/test files (required by NodeNext module resolution) - Add vitest/globals to tsconfig types array for global test API declarations
257 lines
7.5 KiB
TypeScript
257 lines
7.5 KiB
TypeScript
import * as core from '@actions/core'
|
|
import * as canaryDeploymentHelper from '../strategyHelpers/canary/canaryHelper.js'
|
|
import * as SMICanaryDeploymentHelper from '../strategyHelpers/canary/smiCanaryHelper.js'
|
|
import * as PodCanaryHelper from '../strategyHelpers/canary/podCanaryHelper.js'
|
|
import {
|
|
getResources,
|
|
updateManifestFiles
|
|
} from '../utilities/manifestUpdateUtils.js'
|
|
import {annotateAndLabelResources} from '../strategyHelpers/deploymentHelper.js'
|
|
import * as models from '../types/kubernetesTypes.js'
|
|
import * as KubernetesManifestUtility from '../utilities/manifestStabilityUtils.js'
|
|
import {
|
|
deleteGreenObjects,
|
|
getManifestObjects,
|
|
NONE_LABEL_VALUE
|
|
} from '../strategyHelpers/blueGreen/blueGreenHelper.js'
|
|
|
|
import {BlueGreenManifests} from '../types/blueGreenTypes.js'
|
|
import {DeployResult} from '../types/deployResult.js'
|
|
|
|
import {
|
|
promoteBlueGreenIngress,
|
|
promoteBlueGreenService,
|
|
promoteBlueGreenSMI
|
|
} from '../strategyHelpers/blueGreen/promote.js'
|
|
|
|
import {
|
|
routeBlueGreenService,
|
|
routeBlueGreenIngressUnchanged,
|
|
routeBlueGreenSMI
|
|
} from '../strategyHelpers/blueGreen/route.js'
|
|
|
|
import {cleanupSMI} from '../strategyHelpers/blueGreen/smiBlueGreenHelper.js'
|
|
import {Kubectl, Resource} from '../types/kubectl.js'
|
|
import {DeploymentStrategy} from '../types/deploymentStrategy.js'
|
|
import {
|
|
parseTrafficSplitMethod,
|
|
TrafficSplitMethod
|
|
} from '../types/trafficSplitMethod.js'
|
|
import {parseRouteStrategy, RouteStrategy} from '../types/routeStrategy.js'
|
|
import {ClusterType} from '../inputUtils.js'
|
|
|
|
export async function promote(
|
|
kubectl: Kubectl,
|
|
manifests: string[],
|
|
deploymentStrategy: DeploymentStrategy,
|
|
resourceType: ClusterType,
|
|
timeout?: string
|
|
) {
|
|
switch (deploymentStrategy) {
|
|
case DeploymentStrategy.CANARY:
|
|
await promoteCanary(kubectl, manifests, timeout)
|
|
break
|
|
case DeploymentStrategy.BLUE_GREEN:
|
|
await promoteBlueGreen(kubectl, manifests, resourceType, timeout)
|
|
break
|
|
default:
|
|
throw Error('Invalid promote deployment strategy')
|
|
}
|
|
}
|
|
|
|
async function promoteCanary(
|
|
kubectl: Kubectl,
|
|
manifests: string[],
|
|
timeout?: string
|
|
) {
|
|
let includeServices = false
|
|
|
|
const manifestFilesForDeployment: string[] = updateManifestFiles(manifests)
|
|
|
|
const trafficSplitMethod = parseTrafficSplitMethod(
|
|
core.getInput('traffic-split-method', {required: true})
|
|
)
|
|
let promoteResult: DeployResult
|
|
let filesToAnnotate: string[]
|
|
if (trafficSplitMethod == TrafficSplitMethod.SMI) {
|
|
includeServices = true
|
|
|
|
// 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
|
|
core.startGroup('Redirecting traffic to canary deployment')
|
|
await SMICanaryDeploymentHelper.redirectTrafficToCanaryDeployment(
|
|
kubectl,
|
|
manifests,
|
|
timeout
|
|
)
|
|
core.endGroup()
|
|
|
|
core.startGroup(
|
|
'Deploying input manifests with SMI canary strategy from promote'
|
|
)
|
|
|
|
promoteResult = await SMICanaryDeploymentHelper.deploySMICanary(
|
|
manifestFilesForDeployment,
|
|
kubectl,
|
|
true,
|
|
timeout
|
|
)
|
|
|
|
core.endGroup()
|
|
|
|
core.startGroup('Redirecting traffic to stable deployment')
|
|
const stableRedirectManifests =
|
|
await SMICanaryDeploymentHelper.redirectTrafficToStableDeployment(
|
|
kubectl,
|
|
manifests,
|
|
timeout
|
|
)
|
|
|
|
filesToAnnotate = promoteResult.manifestFiles.concat(
|
|
stableRedirectManifests
|
|
)
|
|
|
|
core.endGroup()
|
|
} else {
|
|
core.startGroup('Deploying input manifests from promote')
|
|
promoteResult = await PodCanaryHelper.deployPodCanary(
|
|
manifestFilesForDeployment,
|
|
kubectl,
|
|
true,
|
|
timeout
|
|
)
|
|
filesToAnnotate = promoteResult.manifestFiles
|
|
core.endGroup()
|
|
}
|
|
|
|
core.startGroup('Deleting canary and baseline workloads')
|
|
try {
|
|
await canaryDeploymentHelper.deleteCanaryDeployment(
|
|
kubectl,
|
|
manifests,
|
|
includeServices
|
|
)
|
|
} catch (ex) {
|
|
core.warning(
|
|
`Exception occurred while deleting canary and baseline workloads: ${ex}`
|
|
)
|
|
}
|
|
core.endGroup()
|
|
|
|
// annotate resources
|
|
core.startGroup('Annotating resources')
|
|
const resources: Resource[] = getResources(
|
|
filesToAnnotate,
|
|
models.DEPLOYMENT_TYPES.concat([
|
|
models.DiscoveryAndLoadBalancerResource.SERVICE
|
|
])
|
|
)
|
|
await annotateAndLabelResources(filesToAnnotate, kubectl, resources)
|
|
core.endGroup()
|
|
}
|
|
|
|
async function promoteBlueGreen(
|
|
kubectl: Kubectl,
|
|
manifests: string[],
|
|
resourceType: ClusterType,
|
|
timeout?: string
|
|
) {
|
|
// update container images and pull secrets
|
|
const inputManifestFiles: string[] = updateManifestFiles(manifests)
|
|
const manifestObjects: BlueGreenManifests =
|
|
getManifestObjects(inputManifestFiles)
|
|
|
|
const routeStrategy = parseRouteStrategy(
|
|
core.getInput('route-method', {required: true})
|
|
)
|
|
|
|
core.startGroup('Deleting old deployment and making new stable deployment')
|
|
|
|
const {deployResult} = await (async () => {
|
|
switch (routeStrategy) {
|
|
case RouteStrategy.INGRESS:
|
|
return await promoteBlueGreenIngress(
|
|
kubectl,
|
|
manifestObjects,
|
|
timeout
|
|
)
|
|
case RouteStrategy.SMI:
|
|
return await promoteBlueGreenSMI(kubectl, manifestObjects, timeout)
|
|
default:
|
|
return await promoteBlueGreenService(
|
|
kubectl,
|
|
manifestObjects,
|
|
timeout
|
|
)
|
|
}
|
|
})()
|
|
|
|
core.endGroup()
|
|
|
|
// checking stability of newly created deployments
|
|
core.startGroup('Checking manifest stability')
|
|
const deployedManifestFiles = deployResult.manifestFiles
|
|
const resources: Resource[] = getResources(
|
|
deployedManifestFiles,
|
|
models.DEPLOYMENT_TYPES.concat([
|
|
models.DiscoveryAndLoadBalancerResource.SERVICE
|
|
])
|
|
)
|
|
await KubernetesManifestUtility.checkManifestStability(
|
|
kubectl,
|
|
resources,
|
|
resourceType,
|
|
timeout
|
|
)
|
|
core.endGroup()
|
|
|
|
core.startGroup(
|
|
'Routing to new deployments and deleting old workloads and services'
|
|
)
|
|
if (routeStrategy == RouteStrategy.INGRESS) {
|
|
await routeBlueGreenIngressUnchanged(
|
|
kubectl,
|
|
manifestObjects.serviceNameMap,
|
|
manifestObjects.ingressEntityList
|
|
)
|
|
|
|
await deleteGreenObjects(
|
|
kubectl,
|
|
[].concat(
|
|
manifestObjects.deploymentEntityList,
|
|
manifestObjects.serviceEntityList
|
|
),
|
|
timeout
|
|
)
|
|
} else if (routeStrategy == RouteStrategy.SMI) {
|
|
await routeBlueGreenSMI(
|
|
kubectl,
|
|
NONE_LABEL_VALUE,
|
|
manifestObjects.serviceEntityList
|
|
)
|
|
await deleteGreenObjects(
|
|
kubectl,
|
|
manifestObjects.deploymentEntityList,
|
|
timeout
|
|
)
|
|
await cleanupSMI(kubectl, manifestObjects.serviceEntityList, timeout)
|
|
} else {
|
|
await routeBlueGreenService(
|
|
kubectl,
|
|
NONE_LABEL_VALUE,
|
|
manifestObjects.serviceEntityList
|
|
)
|
|
await deleteGreenObjects(
|
|
kubectl,
|
|
manifestObjects.deploymentEntityList,
|
|
timeout
|
|
)
|
|
}
|
|
core.endGroup()
|
|
|
|
// annotate resources
|
|
core.startGroup('Annotating resources')
|
|
await annotateAndLabelResources(deployedManifestFiles, kubectl, resources)
|
|
core.endGroup()
|
|
}
|