🌳
pt0/deployF/k8sF/genericPvcF.mts
1import * as _ from 'lodash-es'
3import { getKlusterCtx, getCurKlusterCfg, type KubeResource } from './ctxF/klusterCtxF.mts'
7import { StorageV1Api } from '@kubernetes/client-node'
8import { getKubeApis } from './getApisF.mts'
11type PvcSizeValidator = (sizeGb: number) => void
12const sizeValidatorH: Record<string, PvcSizeValidator> = {}
13export const registerPvcSizeValidator = (cloudName: string, validator: PvcSizeValidator) => {
14 sizeValidatorH[cloudName] = validator
16export const validatePvcSizeForCloud = (k8sCloudName: string | undefined, sizeGb: number) => {
17 if (!k8sCloudName) return
18 const validator = sizeValidatorH[k8sCloudName]
19 if (validator) validator(sizeGb)
22const listStorageClassNames = async ({cluster_name}: {cluster_name: string}) => {
23 const {kubeConfig} = await getKubeApis({cluster_name})
24 const storageV1Api = kubeConfig.makeApiClient(StorageV1Api)
25 const {items} = await storageV1Api.listStorageClass()
26 return _.map(items, 'metadata.name')
29export const getOptStorClassName = async ({cluster_name, name}: {cluster_name: string, name?: string}): Promise<string | undefined> => {
30 if (!name) return undefined
31 const storageClassNames = await listStorageClassNames({cluster_name})
32 return storageClassNames.includes(name) ? name : undefined
35export const pvcTypeCtx = genContext<{ pvcReplicaCnt?: number, pvcEncName?: string }>()
37type GenericPvcTmplAProps = {
38 useM2?: boolean
39 storageClassName?: string
40 pvcStorClassName?: string
41 name: string
42 sizeGb: number
43 softdeleted?: boolean
44 pvcReplicaCnt?: number
45 [key: string]: any
48export const genericPvcTmplA = async ({
49 useM2, storageClassName: deprecStorageClassName,
50 pvcStorClassName,
51 ...props
52}: GenericPvcTmplAProps): Promise<KubeResource[]> => {
53 // prefer kubeActionPvc since it sets softdeleted
55 const pvcTypeStore = pvcTypeCtx.getStore()
56 let {pvcReplicaCnt} = _.merge({}, pvcTypeStore, props) as { pvcReplicaCnt?: number }
58 const {cluster_name, pvcStorClassName: _pvcStorClassName} = getKlusterCtx(props)
59 pvcStorClassName ||= _pvcStorClassName
60 const action = getAction()
61 throwIf(() => deprecStorageClassName)
63 const {k8sCloudName, pvcReplicaCnt: _pvcReplicaCnt=1, storClassGenFn} = getCurKlusterCfg(cluster_name)
64 pvcReplicaCnt ||= _pvcReplicaCnt
66 if (action == 'apply') validatePvcSizeForCloud(k8sCloudName, props.sizeGb)
68 const resources: KubeResource[] = []
70 if (!pvcStorClassName && storClassGenFn) {
71 ;({pvcStorClassName} = storClassGenFn({useM2, pvcReplicaCnt, ...pvcTypeStore}))
72 }
74 if (action == 'apply' && pvcStorClassName) {
75 const storageClassNames = await listStorageClassNames({cluster_name})
76 assertIncludes(storageClassNames, pvcStorClassName, {
77 pvcStorClassName, storageClassNames, cluster_name,
78 msg: 'required storageclass does not exist on cluster'
79 })
80 }
82 return [
83 ...resources,
84 deprecGenericPvc({storageClassName: pvcStorClassName, ...props}) as KubeResource,
85 ]
88type DeprecGenericPvcProps = {
89 name: string
90 sizeGb: number
91 softdeleted?: boolean
92 storageClassName?: string
93 accessModes?: string[]
96export const deprecGenericPvc = ({
97 name, sizeGb, softdeleted, storageClassName, accessModes = ['ReadWriteOnce'],
98}: DeprecGenericPvcProps) => { // prefer genericPvcTmplA
100 return {
101 apiVersion: 'v1',
102 kind: 'PersistentVolumeClaim',
103 metadata: {
104 name,
105 labels: {
106 softdeleted_since: softdeleted ? _.round(luxNow().toSeconds()).toString() : 'false'
107 },
108 },
109 spec: {
110 storageClassName,
111 accessModes,
112 resources: {
113 requests: {
114 storage: `${sizeGb}Gi`
115 }
116 },
117 }
118 }