1import * as _ from 'lodash-es' 8import type { V1Volume, V1VolumeMount, V1HostAlias, V1SecurityContext, V1PodSecurityContext, V1Toleration, V1EnvVarSource } from '@kubernetes/client-node' 10/** Pod-level options shared by all workload kinds */ 11export type PodSpecOpts = { 12 securityContext?: V1SecurityContext // container-level (privileged/capabilities/runAs*) 13 podSecurityContext?: V1PodSecurityContext 14 tolerations?: V1Toleration[] 18 nodeSelector?: Record<string, string> 19 serviceAccountName?: string 22export type DeploymentJob = PodSpecOpts & { 27export type DaemonSetJob = PodSpecOpts & { 29 updateStrategy?: object 31export type SingleJob = PodSpecOpts & { 34 ttlSecondsAfterFinished?: number 36export type CronJobJob = PodSpecOpts & { 39 concurrencyPolicy?: string 41 activeDeadlineSec?: number 44export type JobType = DeploymentJob | DaemonSetJob | SingleJob | CronJobJob 46/** DRY defaults for periodic idempotent sync cronjobs: replace any stuck run each tick, kill stuck runs well under the 5-min interval so a degraded cluster can't pile up unbounded Pending pods. */ 47export const syncCronJobPolicy = {concurrencyPolicy: 'Replace' as const, activeDeadlineSec: 240} 49type ResourceForJobTypeProps = { 57 volumeMounts?: V1VolumeMount[] 61 colocateWithPod?: string 62 envLocal: Record<string, string | V1EnvVarSource> 65export const hostAliasesCtx = genContext<{hostAliases?: V1HostAlias[]}>() 67export const resourceForJobType = async ({jobType, name, cluster_name, dockreg_host, image, taskCmd, volumes, volumeMounts, git_sha, action, hasPvc, colocateWithPod, envLocal}: ResourceForJobTypeProps) => { 69 const {kind, securityContext, podSecurityContext, tolerations, hostNetwork, hostPID, hostIPC, nodeSelector, serviceAccountName} = jobType 71 const {hostAliases} = hostAliasesCtx.getStore() || {} 77 // PodSpec shared by all kinds. PVC-colocation affinity is Deployment-only (meaningless for DaemonSet one-per-node), applied in the Deployment branch. 78 const spec: Record<string, unknown> = { 82 ...(podSecurityContext && {securityContext: podSecurityContext}), 83 ...(tolerations && {tolerations}), 84 ...(hostNetwork && {hostNetwork}), 85 ...(hostPID && {hostPID}), 86 ...(hostIPC && {hostIPC}), 87 ...(nodeSelector && {nodeSelector}), 88 ...(serviceAccountName && {serviceAccountName}), 94 ...(securityContext && {securityContext}), 95 command: [ '/bin/sh', '-c' ], 103 if (kind === 'Deployment') { 104 const {replicas, strategy} = jobType 108 requiredDuringSchedulingIgnoredDuringExecution: [ 109 { // ensures pods on same node for ReadWriteOnce 110 // https://stackoverflow.com/questions/65313780/kubernetes-how-to-config-a-group-of-pods-to-be-deployed-on-the-same-node 111 topologyKey: 'kubernetes.io/hostname', 117 values: [colocateWithPod || name] 127 name, spec, git_sha, jobType: {replicas}, strategy: hasPvc ? {type: 'Recreate', rollingUpdate: null} : strategy 131 if (kind === 'DaemonSet') { 132 const {updateStrategy} = jobType 134 name, spec, git_sha, updateStrategy 138 Object.assign(spec, { 139 restartPolicy: 'Never', 143 const {backoffLimit=0, ttlSecondsAfterFinished} = jobType 145 apiVersion: 'batch/v1', 151 ttlSecondsAfterFinished: ttlSecondsAfterFinished ?? 60 * 60 * 24 * 7, 155 labels: { name, git_sha } 163 if (kind == 'CronJob') { 164 const {schedule, concurrencyPolicy, backoffLimit=0, activeDeadlineSec} = jobType 166 apiVersion: 'batch/v1', 172 schedule, concurrencyPolicy, 173 successfulJobsHistoryLimit: 3, 174 failedJobsHistoryLimit: 3, 177 ttlSecondsAfterFinished: 60 * 60 * 2, 179 activeDeadlineSeconds: activeDeadlineSec, 182 labels: { name, git_sha }