// K8s PersistentVolumeClaim schema + YAML generator for ConfTemplate // Generated: 2026-07-18 export const k8sPvcSchema = { id: 'k8s-pvc', name: 'K8s PersistentVolumeClaim', icon: 'HardDrive', category: '云原生', description: 'Kubernetes 持久化存储声明 (PVC)', format: 'yaml', fileName: 'pvc.yaml', groups: [ { title: '基础信息', fields: [ { key: 'name', label: 'PVC 名称', type: 'text', placeholder: 'my-pvc', default: 'my-pvc', required: true, tip: 'PersistentVolumeClaim 资源名称', }, { key: 'namespace', label: '命名空间', type: 'text', placeholder: 'default', default: 'default', }, { key: 'accessModes', label: '访问模式', type: 'select', options: [ { label: 'ReadWriteOnce', value: 'ReadWriteOnce' }, { label: 'ReadWriteMany', value: 'ReadWriteMany' }, { label: 'ReadOnlyMany', value: 'ReadOnlyMany' }, ], default: 'ReadWriteOnce', tip: '单节点读写 / 多节点读写 / 多节点只读', }, { key: 'storageClassName', label: 'StorageClass 名称', type: 'text', placeholder: 'standard', default: 'standard', }, { key: 'storageRequest', label: '请求存储容量', type: 'text', placeholder: '10Gi', default: '10Gi', required: true, tip: '例如 10Gi, 100Mi', }, { key: 'volumeMode', label: '卷模式', type: 'select', options: [ { label: 'Filesystem', value: 'Filesystem' }, { label: 'Block', value: 'Block' }, ], default: 'Filesystem', }, ], }, ], } function yamlScalar(v) { if (v === null || v === undefined) return '' if (typeof v === 'number' || typeof v === 'boolean') return String(v) const s = String(v) if (/[:#\n"'`]|^[\s\-]|[\s]$/.test(s) || /^(true|false|null|yes|no|on|off|~)$/i.test(s)) { return JSON.stringify(s) } return s } function isEmptyMapping(o) { if (!o || typeof o !== 'object' || Array.isArray(o)) return false return Object.values(o).every(v => v === null || v === undefined || v === '' || (typeof v === 'object' && isEmptyMapping(v))) } function toYaml(obj, baseIndent = '') { const lines = [] for (const [key, value] of Object.entries(obj)) { if (value === null || value === undefined || value === '') continue if (Array.isArray(value)) { const items = value.filter(v => v !== null && v !== undefined && v !== '') if (!items.length) continue lines.push(`${baseIndent}${key}:`) for (const item of items) { if (item && typeof item === 'object' && !Array.isArray(item)) { if (isEmptyMapping(item)) continue const entries = Object.entries(item).filter(([, v]) => v !== null && v !== undefined && v !== '') if (!entries.length) continue const childIndent = baseIndent + ' ' const subAllIndent = childIndent + ' ' const subObj = {} for (const [k, v] of entries) subObj[k] = v const subRendered = toYaml(subObj, subAllIndent) const subLines = subRendered.split('\n') if (subLines[0].startsWith(subAllIndent)) { subLines[0] = childIndent + '- ' + subLines[0].slice(subAllIndent.length) } else { subLines[0] = childIndent + '- ' + subLines[0].trimStart() } lines.push(...subLines) } else { lines.push(`${baseIndent} - ${yamlScalar(item)}`) } } } else if (typeof value === 'object') { if (isEmptyMapping(value)) { lines.push(`${baseIndent}${key}: {}`) continue } lines.push(`${baseIndent}${key}:`) const childIndent = baseIndent + ' ' lines.push(toYaml(value, childIndent)) } else { lines.push(`${baseIndent}${key}: ${yamlScalar(value)}`) } } return lines.join('\n') } export function generateK8sPvcYaml(config) { const pvc = { apiVersion: 'v1', kind: 'PersistentVolumeClaim', metadata: { name: config.name, namespace: config.namespace, }, spec: { accessModes: [config.accessModes], storageClassName: config.storageClassName, volumeMode: config.volumeMode, resources: { requests: { storage: config.storageRequest, }, }, }, } const header = `# K8s PersistentVolumeClaim - 由 ConfTemplate 生成 # 生成时间: ${new Date().toLocaleString('zh-CN')} # 部署命令: kubectl apply -f ${config.fileName || 'pvc.yaml'} ` return header + '\n' + toYaml(pvc) }