import { k8sUpdate, k8sGet, k8sDelete, K8sModel, K8sResourceKind, } from '@openshift-console/dynamic-plugin-sdk'; const RESOURCE_LIMITS_ANNOTATION = 'devns.openshift.io/resource-limits'; const NamespaceModel: K8sModel = { apiGroup: '', apiVersion: 'v1', kind: 'Namespace', plural: 'namespaces', abbr: 'NS', label: 'Namespace', labelPlural: 'Namespaces', }; const ResourceQuotaModel: K8sModel = { apiGroup: '', apiVersion: 'v1', kind: 'ResourceQuota', plural: 'resourcequotas', abbr: 'RQ', label: 'Resource Quota', labelPlural: 'Resource Quotas', }; const PodModel: K8sModel = { apiGroup: '', apiVersion: 'v1', kind: 'Pod', plural: 'pods', abbr: 'P', label: 'Pod', labelPlural: 'Pods', }; interface ResourceQuota extends K8sResourceKind { spec: { hard: { 'limits.cpu'?: string; 'limits.memory'?: string; 'requests.cpu'?: string; 'requests.memory'?: string; [key: string]: string | undefined; }; }; } interface PodList extends K8sResourceKind { items: K8sResourceKind[]; } export const pauseNamespace = async (namespace: string) => { try { // Get current namespace const ns = await k8sGet({ model: NamespaceModel, name: namespace, }); if (!ns?.metadata) { throw new Error('Invalid namespace resource'); } // Store current resource limits in annotation const resourceQuotas = await k8sGet({ model: ResourceQuotaModel, name: '', // We'll need to handle getting the actual quota name ns: namespace, }); if (!resourceQuotas?.metadata || !resourceQuotas?.spec) { throw new Error('Invalid resource quota'); } // Save current quotas in annotation const nsWithAnnotation: K8sResourceKind = { ...ns, metadata: { ...ns.metadata, annotations: { ...(ns.metadata.annotations || {}), [RESOURCE_LIMITS_ANNOTATION]: JSON.stringify(resourceQuotas), }, }, }; await k8sUpdate({ model: NamespaceModel, data: nsWithAnnotation, }); // Set resource quotas to 0 const updatedQuotas: ResourceQuota = { ...resourceQuotas, spec: { ...resourceQuotas.spec, hard: { 'limits.cpu': '0', 'limits.memory': '0', 'requests.cpu': '0', 'requests.memory': '0', }, }, }; await k8sUpdate({ model: ResourceQuotaModel, data: updatedQuotas, }); // Delete all pods const pods = await k8sGet({ model: PodModel, ns: namespace, }); if (pods?.items) { await Promise.all( pods.items.map((pod) => k8sDelete({ model: PodModel, resource: pod, }), ), ); } return true; } catch (error) { console.error('Error pausing namespace:', error); throw error; } }; export const unpauseNamespace = async (namespace: string) => { try { // Get namespace const ns = await k8sGet({ model: NamespaceModel, name: namespace, }); if (!ns?.metadata?.annotations) { throw new Error('Namespace metadata or annotations not found'); } // Get stored resource limits const storedLimits = ns.metadata.annotations[RESOURCE_LIMITS_ANNOTATION]; if (!storedLimits) { throw new Error('No stored resource limits found'); } const resourceQuotas: ResourceQuota = JSON.parse(storedLimits); // Restore resource quotas await k8sUpdate({ model: ResourceQuotaModel, data: resourceQuotas, }); // Remove stored limits annotation const { [RESOURCE_LIMITS_ANNOTATION]: removed, ...remainingAnnotations } = ns.metadata.annotations; const nsWithoutAnnotation: K8sResourceKind = { ...ns, metadata: { ...ns.metadata, annotations: remainingAnnotations, }, }; await k8sUpdate({ model: NamespaceModel, data: nsWithoutAnnotation, }); return true; } catch (error) { console.error('Error unpausing namespace:', error); throw error; } };