🌳
pt0/nextF/deployF/mkNextDeploymentF.mts
1import * as _ from 'lodash-es'
19const pvcPodAffinity = (colocateWith: string) => ({
20 podAffinity: {
21 requiredDuringSchedulingIgnoredDuringExecution: [{
22 topologyKey: 'kubernetes.io/hostname',
23 labelSelector: {
24 matchExpressions: [{ key: 'name', operator: 'In', values: [colocateWith] }]
25 }
26 }]
27 }
28})
30export type DeployAppCfg = AppCfg & { envLocal?: Record<string, string>; webReplicas?: number; deployStrategy?: {type: string}; monorDataPvcH?: unknown; colocateWithPod?: string; rolloutTimeoutMs?: number }
32const mkNextDeployment = async (appConfig: DeployAppCfg) => {
33 const { name, cluster_name, secretNames=[], envLocal, git_sha, buildSha, webReplicas: replicas=1, action='apply',
34 deployStrategy, monorDataPvcH, colocateWithPod, rolloutTimeoutMs,
35 } = appConfig
36 assertDefined(name, {appConfig})
37 assertDefined(cluster_name, {appConfig})
39 const hasPvc = monorDataPvcH && cluster_name != 'minik'
40 let strategy = deployStrategy as any
41 let affinity: object | undefined
42 if (hasPvc) {
43 if (strategy && strategy.type !== 'Recreate') {
44 throwIf(() => true, {name, deployStrategy, monorDataPvcH, msg: 'pvc-backed app must use Recreate strategy'})
45 }
46 strategy ??= {type: 'Recreate'}
47 affinity = pvcPodAffinity(colocateWithPod || name)
48 }
50 const env = _.map(envLocal, (value, name) => ({name, value}))
52 const deploymentBase = generic3DeploymentTmpl({name, strategy})
54 const {image} = await imageFromAppCfg(appConfig)
56 const envConf = appConfig.envConf as Record<string, string>
57 const {promises, envConfHash, ...volMnts0} = getVolumesForSecrets({
58 secretNames, envConf, cluster_name, name, action,
59 })
60 await Promise.all(promises)
61 const volMntsA = [volMnts0]
62 const volStore = nextVolMntsCtx.getStore()
63 if (volStore) {
64 const {resources: volResources, ...volMnts1} = volStore
65 volMntsA.push(volMnts1)
66 promises.push(resourcesAction({action, resources: volResources}))
67 }
68 const {volumes, volumeMounts} = mergeVolsMnts(...volMntsA)
70 const resources: KubeResource[] = []
71 const initContainers: object[] = []
72 const extraContainers: object[] = []
73 const annotations: Record<string, string> = {}
75 const store = ptKubeCtx.getStore()
76 if (store?.modVolsFnc) {
77 await (store.modVolsFnc as (args: object) => Promise<void>)({volumes, volumeMounts, initContainers, resources, extraContainers, annotations, image})
78 }
80 const newDeployment = _.merge(deploymentBase, {
81 metadata: {
82 labels: {git_sha}
83 },
84 spec: {
85 replicas,
86 template: {
87 metadata: {
88 labels: {
89 git_sha,
90 },
91 annotations: {
92 envConfHash,
93 ...(buildSha && buildSha !== git_sha && {build_sha: buildSha}),
94 ...annotations,
95 },
96 },
97 spec: {
98 affinity,
99 initContainers,
100 containers: [
101 {
102 name: 'ptcont',
103 image,
104 env,
105 volumeMounts,
106 ...nextContCtx.getStore(),
107 },
108 ...extraContainers,
109 ],
110 volumes,
111 imagePullSecrets: await getImagePullSecrets(),
112 }
113 }
114 }
115 })
117 resources.push(newDeployment)
118 await resourcesAction({resources, action, cluster_name})
120 return newDeployment
123export const applyAndWaitRollout = async (appConfig: DeployAppCfg) => {
124 const {cluster_name, action='apply', rolloutTimeoutMs} = appConfig
125 if (isActionSupported(action)) assertDefined(cluster_name, {appConfig})
126 if (action === 'apply') initCliTimer()
127 if (action === 'apply') printActionEta(getAppCfg()?.importMetaUrl || '', 'apply')
128 const startTime = Date.now()
130 let resource: KubeResource | undefined
131 if (isActionSupported(action)) {
132 resource = await mkNextDeployment(appConfig)
133 }
135 if (action === 'apply' && resource) {
136 await waitForDeploymentRollout({resource, cluster_name: cluster_name!, rolloutTimeoutMs})
137 const appCfg = getAppCfg()
138 recordApplyTiming({ep: appCfg?.importMetaUrl || '', startTime, gitSha: appCfg?.git_sha || '', buildSha: appCfg?.buildSha, imageReused: appCfg?.imageReused})
139 }