// container-utils.ts — K8s container spec ↔ form 转换工具 // 作者: Logan import type { ContainerForm, ProbeDef, LifecycleHandler, } from './types'; import type { JsonData } from '@/types/common'; // K8s container spec → form conversion // Logan.Li export function toContainerForm(c: JsonData): ContainerForm { const probe = (p: JsonData): ProbeDef | null => { if (!p) return null; let type: ProbeDef['type'] = 'none'; if (p.httpGet) type = 'httpGet'; else if (p.tcpSocket) type = 'tcpSocket'; else if (p.exec) type = 'exec'; return { type, httpPath: p.httpGet?.path || '', httpPort: p.httpGet?.port ?? '', httpHost: p.httpGet?.host || '', tcpPort: p.tcpSocket?.port ?? '', execCommand: Array.isArray(p.exec?.command) ? p.exec.command.join('\n') : (p.exec?.command || ''), initialDelaySeconds: p.initialDelaySeconds, periodSeconds: p.periodSeconds, timeoutSeconds: p.timeoutSeconds, successThreshold: p.successThreshold, failureThreshold: p.failureThreshold, }; }; const lh = (h: JsonData): LifecycleHandler => { if (!h) return { type: 'none' }; let type: LifecycleHandler['type'] = 'none'; if (h.exec) type = 'exec'; else if (h.httpGet) type = 'httpGet'; else if (h.tcpSocket) type = 'tcpSocket'; return { type, execCommand: Array.isArray(h.exec?.command) ? h.exec.command.join('\n') : '', httpPath: h.httpGet?.path || '', httpPort: h.httpGet?.port ?? '', tcpPort: h.tcpSocket?.port ?? '', }; }; const sc = c.securityContext; return { name: c.name || '', image: c.image || '', imagePullPolicy: c.imagePullPolicy || '', command: Array.isArray(c.command) ? c.command : [], args: Array.isArray(c.args) ? c.args : [], workingDir: c.workingDir || '', env: (Array.isArray(c.env) ? c.env : []).map((e: JsonData) => ({ name: e.name || '', value: e.value ?? '' })), ports: (Array.isArray(c.ports) ? c.ports : []).map((p: JsonData) => ({ name: p.name || '', containerPort: p.containerPort ?? '', protocol: p.protocol || 'TCP' })), resources: { requests: c.resources?.requests || {}, limits: c.resources?.limits || {}, }, livenessProbe: probe(c.livenessProbe), readinessProbe: probe(c.readinessProbe), startupProbe: probe(c.startupProbe), lifecycle: c.lifecycle ? { postStart: c.lifecycle.postStart ? lh(c.lifecycle.postStart) : undefined, preStop: c.lifecycle.preStop ? lh(c.lifecycle.preStop) : undefined, } : null, securityContext: sc ? { runAsUser: sc.runAsUser, runAsGroup: sc.runAsGroup, runAsNonRoot: sc.runAsNonRoot, privileged: sc.privileged, readOnlyRootFilesystem: sc.readOnlyRootFilesystem, allowPrivilegeEscalation: sc.allowPrivilegeEscalation, capabilitiesAdd: sc.capabilities?.add || [], capabilitiesDrop: sc.capabilities?.drop || [], } : null, volumeMounts: (Array.isArray(c.volumeMounts) ? c.volumeMounts : []).map((m: JsonData) => ({ name: m.name || '', mountPath: m.mountPath || '', subPath: m.subPath || '', readOnly: m.readOnly || false, })), }; } // form → K8s container spec // Logan.Li export function emptyContainer(name = ''): ContainerForm { return { name, image: '', imagePullPolicy: 'IfNotPresent', command: [], args: [], workingDir: '', env: [], ports: [], resources: {}, livenessProbe: null, readinessProbe: null, startupProbe: null, lifecycle: null, securityContext: null, volumeMounts: [], }; } export function probeToK8s(p: ProbeDef | null | undefined): JsonData { if (!p || p.type === 'none' || !p.type) return undefined; const out: JsonData = {}; if (p.type === 'httpGet') { out.httpGet = {}; if (p.httpPath) out.httpGet.path = p.httpPath; if (p.httpPort !== '' && p.httpPort != null) out.httpGet.port = typeof p.httpPort === 'number' ? p.httpPort : Number(p.httpPort); if (p.httpHost) out.httpGet.host = p.httpHost; } else if (p.type === 'tcpSocket') { out.tcpSocket = {}; if (p.tcpPort !== '' && p.tcpPort != null) out.tcpSocket.port = typeof p.tcpPort === 'number' ? p.tcpPort : Number(p.tcpPort); } else if (p.type === 'exec') { const cmds = (p.execCommand || '').split('\n').map(s => s.trim()).filter(Boolean); if (cmds.length) out.exec = { command: cmds }; } if (p.initialDelaySeconds != null) out.initialDelaySeconds = p.initialDelaySeconds; if (p.periodSeconds != null) out.periodSeconds = p.periodSeconds; if (p.timeoutSeconds != null) out.timeoutSeconds = p.timeoutSeconds; if (p.successThreshold != null) out.successThreshold = p.successThreshold; if (p.failureThreshold != null) out.failureThreshold = p.failureThreshold; return Object.keys(out).length > 0 ? out : undefined; } export function handlerToK8s(h: LifecycleHandler | undefined): JsonData { if (!h || h.type === 'none' || !h.type) return undefined; if (h.type === 'exec') { const cmds = (h.execCommand || '').split('\n').map(s => s.trim()).filter(Boolean); return cmds.length ? { exec: { command: cmds } } : undefined; } if (h.type === 'httpGet') { const o: JsonData = { httpGet: {} }; if (h.httpPath) o.httpGet.path = h.httpPath; if (h.httpPort !== '' && h.httpPort != null) o.httpGet.port = Number(h.httpPort); return o; } if (h.type === 'tcpSocket') { const o: JsonData = { tcpSocket: {} }; if (h.tcpPort !== '' && h.tcpPort != null) o.tcpSocket.port = Number(h.tcpPort); return o; } return undefined; } export function containerToK8s(c: ContainerForm): JsonData { const out: JsonData = { name: c.name }; if (c.image) out.image = c.image; if (c.imagePullPolicy) out.imagePullPolicy = c.imagePullPolicy; if (c.workingDir) out.workingDir = c.workingDir; if (c.command.length > 0) out.command = c.command; if (c.args.length > 0) out.args = c.args; if (c.env.length > 0) { out.env = c.env.filter(e => e.name.trim()).map(e => { const o: JsonData = { name: e.name }; if (e.value != null) o.value = e.value; return o; }); } if (c.ports.length > 0) { out.ports = c.ports.filter(p => p.containerPort !== '').map(p => { const o: JsonData = { containerPort: typeof p.containerPort === 'number' ? p.containerPort : Number(p.containerPort) }; if (p.name) o.name = p.name; if (p.protocol) o.protocol = p.protocol; return o; }); } const req = c.resources.requests; const lim = c.resources.limits; const hasReq = req && Object.values(req).some(v => v && v.trim()); const hasLim = lim && Object.values(lim).some(v => v && v.trim()); if (hasReq || hasLim) { out.resources = {}; if (hasReq) { out.resources.requests = {}; if (req!.cpu?.trim()) out.resources.requests.cpu = req!.cpu; if (req!.memory?.trim()) out.resources.requests.memory = req!.memory; } if (hasLim) { out.resources.limits = {}; if (lim!.cpu?.trim()) out.resources.limits.cpu = lim!.cpu; if (lim!.memory?.trim()) out.resources.limits.memory = lim!.memory; } } const lp = probeToK8s(c.livenessProbe); if (lp) out.livenessProbe = lp; const rp = probeToK8s(c.readinessProbe); if (rp) out.readinessProbe = rp; const sp = probeToK8s(c.startupProbe); if (sp) out.startupProbe = sp; if (c.lifecycle) { const lc: JsonData = {}; const ps = handlerToK8s(c.lifecycle.postStart); if (ps) lc.postStart = ps; const pst = handlerToK8s(c.lifecycle.preStop); if (pst) lc.preStop = pst; if (Object.keys(lc).length > 0) out.lifecycle = lc; } if (c.securityContext) { const s = c.securityContext; const sc: JsonData = {}; if (s.runAsUser !== '' && s.runAsUser != null) sc.runAsUser = Number(s.runAsUser); if (s.runAsGroup !== '' && s.runAsGroup != null) sc.runAsGroup = Number(s.runAsGroup); if (s.runAsNonRoot != null) sc.runAsNonRoot = s.runAsNonRoot; if (s.privileged != null) sc.privileged = s.privileged; if (s.readOnlyRootFilesystem != null) sc.readOnlyRootFilesystem = s.readOnlyRootFilesystem; if (s.allowPrivilegeEscalation != null) sc.allowPrivilegeEscalation = s.allowPrivilegeEscalation; const capAdd = (s.capabilitiesAdd || []).filter(Boolean); const capDrop = (s.capabilitiesDrop || []).filter(Boolean); if (capAdd.length || capDrop.length) { sc.capabilities = {}; if (capAdd.length) sc.capabilities.add = capAdd; if (capDrop.length) sc.capabilities.drop = capDrop; } if (Object.keys(sc).length > 0) out.securityContext = sc; } if (c.volumeMounts.length > 0) { out.volumeMounts = c.volumeMounts.filter(m => m.name.trim() && m.mountPath.trim()).map(m => { const o: JsonData = { name: m.name, mountPath: m.mountPath }; if (m.subPath) o.subPath = m.subPath; if (m.readOnly) o.readOnly = true; return o; }); } return out; } // String array helper (command/args editing) // Logan.Li export function arrToText(arr: string[]): string { return arr.join('\n'); } export function textToArr(text: string): string[] { return text.split('\n').map(s => s.trim()).filter(Boolean); }