Skip to main content
Glama
ooples

MCP Console Automation Server

kubernetes.test.ts31.1 kB
/** * Kubernetes Protocol Integration Tests * Production-ready comprehensive test suite for Kubernetes protocol */ import { describe, test, expect, beforeAll, afterAll, beforeEach, afterEach } from '@jest/globals'; import { KubernetesProtocol } from '../../../src/protocols/KubernetesProtocol.js'; import { MockKubernetesProtocol } from '../../utils/protocol-mocks.js'; import { TestServerManager, createTestServer } from '../../utils/test-servers.js'; import { KubernetesSession, KubernetesProtocolConfig, ConsoleOutput, K8sPod, K8sNamespace, K8sDeployment, K8sService, K8sClusterInfo } from '../../../src/types/index.js'; // Skip cloud protocol tests if SKIP_HARDWARE_TESTS is set (requires Kubernetes cluster) const describeIfCloud = process.env.SKIP_HARDWARE_TESTS ? describe.skip : describe; describeIfCloud('Kubernetes Protocol Integration Tests', () => { let k8sProtocol: KubernetesProtocol; let mockK8sProtocol: MockKubernetesProtocol; let testServerManager: TestServerManager; let mockConfig: KubernetesProtocolConfig; beforeAll(async () => { // Initialize test environment testServerManager = new TestServerManager(); // Create mock Kubernetes API server const k8sApiServerConfig = createTestServer() .protocol('https') .port(6443) .host('127.0.0.1') .withLogging(true) .withBehavior({ responseDelay: 150, errorRate: 0.02 // 2% error rate }) .build(); testServerManager.createServer('k8s-api-server', k8sApiServerConfig); await testServerManager.startServer('k8s-api-server'); // Initialize mock Kubernetes protocol mockK8sProtocol = new MockKubernetesProtocol(); await mockK8sProtocol.start(); // Production Kubernetes protocol configuration mockConfig = { kubeconfig: { clusters: [{ name: 'test-cluster', cluster: { server: 'https://127.0.0.1:6443', 'certificate-authority-data': 'LS0tLS1CRUdJTi...', 'insecure-skip-tls-verify': true } }], contexts: [{ name: 'test-context', context: { cluster: 'test-cluster', user: 'test-user', namespace: 'default' } }], users: [{ name: 'test-user', user: { 'client-certificate-data': 'LS0tLS1CRUdJTi...', 'client-key-data': 'LS0tLS1CRUdJTi...' } }], 'current-context': 'test-context' }, defaultNamespace: 'default', timeout: 30000, maxConcurrentSessions: 20, retryAttempts: 3, retryDelay: 1000, monitoring: { enableMetrics: true, metricsInterval: 10000, collectPodMetrics: true, collectNodeMetrics: true, collectClusterMetrics: true, enableHealthChecks: true, healthCheckInterval: 15000 }, security: { rbacEnabled: true, allowedNamespaces: ['default', 'kube-system', 'test-namespace'], restrictedResources: ['secrets', 'configmaps'], allowExec: true, allowPortForward: true, allowLogAccess: true, serviceAccountName: 'console-automation', enableNetworkPolicies: true }, resource: { defaultCpuLimit: '500m', defaultMemoryLimit: '512Mi', defaultCpuRequest: '100m', defaultMemoryRequest: '128Mi', enableResourceQuotas: true, enableLimitRanges: true }, logging: { enableAuditLogging: true, logLevel: 'info', logFormat: 'json', maxLogFiles: 10, maxLogFileSize: '100MB' }, networking: { defaultServiceType: 'ClusterIP', allowLoadBalancer: false, allowNodePort: true, allowIngress: true, ingressClass: 'nginx', networkPlugin: 'calico' }, storage: { defaultStorageClass: 'standard', allowedStorageClasses: ['standard', 'ssd', 'fast'], enablePersistentVolumes: true, volumeSnapshotEnabled: true } }; // Initialize real Kubernetes protocol for production testing k8sProtocol = new KubernetesProtocol(mockConfig); }); afterAll(async () => { // Cleanup resources await k8sProtocol.cleanup(); await mockK8sProtocol.stop(); await testServerManager.stopAllServers(); }); beforeEach(() => { jest.clearAllMocks(); }); afterEach(async () => { // Clean up any test sessions const sessions = k8sProtocol.getAllSessions(); for (const session of sessions) { try { await k8sProtocol.terminateSession(session.id); } catch (error) { // Ignore cleanup errors } } }); describe('Cluster Connection and Authentication', () => { test('should connect to Kubernetes cluster successfully', async () => { const isConnected = await k8sProtocol.isClusterReachable(); expect(isConnected).toBe(true); const clusterInfo = await k8sProtocol.getClusterInfo(); expect(clusterInfo).toBeDefined(); expect(clusterInfo.version).toBeDefined(); expect(clusterInfo.nodeCount).toBeGreaterThan(0); }); test('should authenticate with cluster successfully', async () => { const authStatus = await k8sProtocol.checkAuthentication(); expect(authStatus.authenticated).toBe(true); expect(authStatus.user).toBeDefined(); expect(authStatus.permissions).toBeDefined(); }); test('should handle invalid kubeconfig', async () => { const invalidConfig = { ...mockConfig, kubeconfig: { ...mockConfig.kubeconfig, 'current-context': 'invalid-context' } }; const invalidProtocol = new KubernetesProtocol(invalidConfig); await expect(invalidProtocol.isClusterReachable()).resolves.toBe(false); await invalidProtocol.cleanup(); }); test('should handle cluster connection timeout', async () => { const timeoutConfig = { ...mockConfig, timeout: 1000, kubeconfig: { ...mockConfig.kubeconfig, clusters: [{ ...mockConfig.kubeconfig.clusters[0], cluster: { ...mockConfig.kubeconfig.clusters[0].cluster, server: 'https://unreachable-cluster:6443' } }] } }; const timeoutProtocol = new KubernetesProtocol(timeoutConfig); await expect(timeoutProtocol.isClusterReachable()).resolves.toBe(false); await timeoutProtocol.cleanup(); }); }); describe('Pod Management and Exec Sessions', () => { let testPod: K8sPod; beforeEach(async () => { // Create a test pod for exec sessions testPod = await k8sProtocol.createPod({ name: `test-pod-${Date.now()}`, namespace: 'default', image: 'busybox', command: ['sleep', '300'], labels: { 'test': 'kubernetes-integration' } }); }); afterEach(async () => { // Cleanup test pod if (testPod) { try { await k8sProtocol.deletePod(testPod.metadata.name, testPod.metadata.namespace); } catch (error) { // Ignore cleanup errors } } }); test('should create exec session in pod successfully', async () => { const sessionOptions = { command: '/bin/sh', args: ['-c', 'echo "Hello Kubernetes"'], consoleType: 'kubernetes' as const, streaming: true, podName: testPod.metadata.name, namespace: testPod.metadata.namespace, containerName: 'main', tty: true, stdin: true }; const session = await k8sProtocol.createSession(sessionOptions); expect(session).toBeDefined(); expect(session.id).toBeDefined(); expect(session.podName).toBe(testPod.metadata.name); expect(session.namespace).toBe('default'); expect(session.containerName).toBe('main'); expect(session.status).toBe('running'); expect(session.type).toBe('kubernetes'); expect(session.isExecSession).toBe(true); expect(session.command).toBe('/bin/sh'); expect(session.args).toEqual(['-c', 'echo "Hello Kubernetes"']); }, 20000); test('should handle pod not found error', async () => { await expect(k8sProtocol.createSession({ command: '/bin/sh', consoleType: 'kubernetes' as const, podName: 'nonexistent-pod', namespace: 'default', containerName: 'main' })).rejects.toThrow('Pod not found'); }); test('should handle container not found error', async () => { await expect(k8sProtocol.createSession({ command: '/bin/sh', consoleType: 'kubernetes' as const, podName: testPod.metadata.name, namespace: testPod.metadata.namespace, containerName: 'nonexistent-container' })).rejects.toThrow('Container not found'); }); test('should list pods in namespace', async () => { const pods = await k8sProtocol.listPods('default'); expect(pods).toBeInstanceOf(Array); expect(pods.length).toBeGreaterThan(0); const createdPod = pods.find(p => p.metadata.name === testPod.metadata.name); expect(createdPod).toBeDefined(); expect(createdPod?.status.phase).toMatch(/^(Pending|Running|Succeeded|Failed|Unknown)$/); }, 15000); test('should get pod details', async () => { const podDetails = await k8sProtocol.getPod(testPod.metadata.name, testPod.metadata.namespace); expect(podDetails).toBeDefined(); expect(podDetails.metadata.name).toBe(testPod.metadata.name); expect(podDetails.metadata.namespace).toBe(testPod.metadata.namespace); expect(podDetails.spec).toBeDefined(); expect(podDetails.status).toBeDefined(); expect(podDetails.spec.containers).toBeInstanceOf(Array); expect(podDetails.spec.containers.length).toBeGreaterThan(0); }); }); describe('Command Execution in Pods', () => { let execSession: KubernetesSession; let testPod: K8sPod; beforeEach(async () => { // Create test pod testPod = await k8sProtocol.createPod({ name: `exec-test-pod-${Date.now()}`, namespace: 'default', image: 'busybox', command: ['sleep', '300'] }); // Wait for pod to be ready await k8sProtocol.waitForPodReady(testPod.metadata.name, testPod.metadata.namespace, 30000); // Create exec session execSession = await k8sProtocol.createSession({ command: '/bin/sh', consoleType: 'kubernetes' as const, podName: testPod.metadata.name, namespace: testPod.metadata.namespace, containerName: 'main' }); }); afterEach(async () => { if (testPod) { await k8sProtocol.deletePod(testPod.metadata.name, testPod.metadata.namespace); } }); test('should execute simple commands successfully', async () => { const result = await k8sProtocol.executeCommand(execSession.id, 'echo "Hello Pod"'); expect(result).toContain('Hello Pod'); }, 15000); test('should execute file system operations', async () => { // Create a test file await k8sProtocol.executeCommand(execSession.id, 'echo "kubernetes test" > /tmp/k8s-test.txt'); // Read the file const result = await k8sProtocol.executeCommand(execSession.id, 'cat /tmp/k8s-test.txt'); expect(result).toContain('kubernetes test'); }, 15000); test('should handle command timeouts', async () => { await expect(k8sProtocol.executeCommand( execSession.id, 'sleep 60', { timeout: 2000 } )).rejects.toThrow('timeout'); }, 5000); test('should capture command output with streaming', async () => { const outputSpy = jest.fn<any>(); k8sProtocol.on('output', outputSpy); await k8sProtocol.executeCommand(execSession.id, 'echo "Stream Test"'); expect(outputSpy).toHaveBeenCalled(); const outputCall = outputSpy.mock.calls.find(call => call[0].data.includes('Stream Test') ); expect(outputCall).toBeDefined(); }, 10000); }); describe('Namespace Management', () => { let testNamespace: string; beforeEach(() => { testNamespace = `test-ns-${Date.now()}`; }); afterEach(async () => { if (testNamespace) { try { await k8sProtocol.deleteNamespace(testNamespace); } catch (error) { // Ignore cleanup errors } } }); test('should list namespaces', async () => { const namespaces = await k8sProtocol.listNamespaces(); expect(namespaces).toBeInstanceOf(Array); expect(namespaces.length).toBeGreaterThan(0); const defaultNs = namespaces.find(ns => ns.metadata.name === 'default'); expect(defaultNs).toBeDefined(); expect(defaultNs?.status.phase).toBe('Active'); }); test('should create and delete namespace', async () => { // Create namespace const createdNs = await k8sProtocol.createNamespace({ name: testNamespace, labels: { 'test': 'integration' } }); expect(createdNs.metadata.name).toBe(testNamespace); expect(createdNs.metadata.labels?.test).toBe('integration'); // Verify namespace exists const namespaces = await k8sProtocol.listNamespaces(); const foundNs = namespaces.find(ns => ns.metadata.name === testNamespace); expect(foundNs).toBeDefined(); // Delete namespace await k8sProtocol.deleteNamespace(testNamespace); // Verify namespace is deleted (may take time) await new Promise(resolve => setTimeout(resolve, 5000)); testNamespace = ''; // Prevent cleanup }, 20000); test('should get namespace details', async () => { const nsDetails = await k8sProtocol.getNamespace('default'); expect(nsDetails).toBeDefined(); expect(nsDetails.metadata.name).toBe('default'); expect(nsDetails.status).toBeDefined(); expect(nsDetails.status.phase).toBe('Active'); }); }); describe('Resource Monitoring and Metrics', () => { let monitoringPod: K8sPod; beforeEach(async () => { monitoringPod = await k8sProtocol.createPod({ name: `monitoring-pod-${Date.now()}`, namespace: 'default', image: 'nginx', resources: { requests: { cpu: '100m', memory: '128Mi' }, limits: { cpu: '500m', memory: '512Mi' } } }); await k8sProtocol.waitForPodReady(monitoringPod.metadata.name, monitoringPod.metadata.namespace, 30000); }); afterEach(async () => { if (monitoringPod) { await k8sProtocol.deletePod(monitoringPod.metadata.name, monitoringPod.metadata.namespace); } }); test('should collect pod metrics', async () => { const metricsSpy = jest.fn<any>(); k8sProtocol.on('pod-metrics', metricsSpy); // Wait for metrics collection await new Promise(resolve => setTimeout(resolve, 12000)); expect(metricsSpy).toHaveBeenCalled(); const metrics = metricsSpy.mock.calls[0][0]; expect(metrics.podName).toBe(monitoringPod.metadata.name); expect(metrics.namespace).toBe(monitoringPod.metadata.namespace); expect(metrics.timestamp).toBeInstanceOf(Date); expect(metrics.cpu).toBeDefined(); expect(metrics.memory).toBeDefined(); expect(typeof metrics.cpu.usage).toBe('number'); expect(typeof metrics.memory.usage).toBe('number'); }, 15000); test('should collect cluster metrics', async () => { const clusterMetricsSpy = jest.fn<any>(); k8sProtocol.on('cluster-metrics', clusterMetricsSpy); // Wait for cluster metrics await new Promise(resolve => setTimeout(resolve, 12000)); expect(clusterMetricsSpy).toHaveBeenCalled(); const clusterMetrics = clusterMetricsSpy.mock.calls[0][0]; expect(clusterMetrics.timestamp).toBeInstanceOf(Date); expect(clusterMetrics.nodes).toBeDefined(); expect(clusterMetrics.pods).toBeDefined(); expect(clusterMetrics.services).toBeDefined(); expect(typeof clusterMetrics.nodes.total).toBe('number'); expect(typeof clusterMetrics.pods.running).toBe('number'); }, 15000); test('should perform health checks', async () => { const healthCheckSpy = jest.fn<any>(); k8sProtocol.on('health-check', healthCheckSpy); // Wait for health checks await new Promise(resolve => setTimeout(resolve, 18000)); expect(healthCheckSpy).toHaveBeenCalled(); const healthCheck = healthCheckSpy.mock.calls[0][0]; expect(healthCheck.timestamp).toBeInstanceOf(Date); expect(['healthy', 'degraded', 'unhealthy']).toContain(healthCheck.status); expect(healthCheck.components).toBeDefined(); expect(healthCheck.components.apiServer).toBeDefined(); expect(healthCheck.components.etcd).toBeDefined(); expect(healthCheck.components.scheduler).toBeDefined(); expect(healthCheck.components.controllerManager).toBeDefined(); }, 20000); }); describe('Log Management', () => { let loggingPod: K8sPod; beforeEach(async () => { loggingPod = await k8sProtocol.createPod({ name: `logging-pod-${Date.now()}`, namespace: 'default', image: 'busybox', command: ['sh', '-c', 'for i in $(seq 1 10); do echo "Log line $i"; sleep 1; done; sleep 300'] }); await k8sProtocol.waitForPodReady(loggingPod.metadata.name, loggingPod.metadata.namespace, 30000); }); afterEach(async () => { if (loggingPod) { await k8sProtocol.deletePod(loggingPod.metadata.name, loggingPod.metadata.namespace); } }); test('should retrieve pod logs', async () => { // Wait for some logs to be generated await new Promise(resolve => setTimeout(resolve, 5000)); const logs = await k8sProtocol.getPodLogs( loggingPod.metadata.name, loggingPod.metadata.namespace, { lines: 5 } ); expect(logs).toBeDefined(); expect(logs.split('\n').length).toBeGreaterThan(0); expect(logs).toContain('Log line'); }, 10000); test('should stream pod logs', async () => { const logSpy = jest.fn<any>(); k8sProtocol.on('pod-log', logSpy); // Start log streaming const stream = await k8sProtocol.streamPodLogs( loggingPod.metadata.name, loggingPod.metadata.namespace ); // Wait for logs await new Promise(resolve => setTimeout(resolve, 8000)); expect(logSpy).toHaveBeenCalled(); const logEntry = logSpy.mock.calls[0][0]; expect(logEntry.podName).toBe(loggingPod.metadata.name); expect(logEntry.namespace).toBe(loggingPod.metadata.namespace); expect(logEntry.timestamp).toBeInstanceOf(Date); expect(logEntry.message).toContain('Log line'); // Stop streaming stream.destroy(); }, 12000); test('should retrieve logs with timestamps', async () => { const logs = await k8sProtocol.getPodLogs( loggingPod.metadata.name, loggingPod.metadata.namespace, { timestamps: true, lines: 3 } ); const lines = logs.split('\n').filter(line => line.trim()); expect(lines.length).toBeGreaterThan(0); // Check if timestamps are present (ISO 8601 format) expect(lines[0]).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/); }, 10000); }); describe('Port Forwarding', () => { let portForwardPod: K8sPod; beforeEach(async () => { portForwardPod = await k8sProtocol.createPod({ name: `port-forward-pod-${Date.now()}`, namespace: 'default', image: 'nginx', ports: [{ containerPort: 80 }] }); await k8sProtocol.waitForPodReady(portForwardPod.metadata.name, portForwardPod.metadata.namespace, 30000); }); afterEach(async () => { if (portForwardPod) { await k8sProtocol.deletePod(portForwardPod.metadata.name, portForwardPod.metadata.namespace); } }); test('should create port forward successfully', async () => { const portForward = await k8sProtocol.createPortForward({ podName: portForwardPod.metadata.name, namespace: portForwardPod.metadata.namespace, localPort: 8080, remotePort: 80 }); expect(portForward).toBeDefined(); expect(portForward.id).toBeDefined(); expect(portForward.localPort).toBe(8080); expect(portForward.remotePort).toBe(80); expect(portForward.status).toBe('active'); // Test connection await new Promise(resolve => setTimeout(resolve, 2000)); // Cleanup await k8sProtocol.stopPortForward(portForward.id); }, 15000); test('should list active port forwards', async () => { const portForward = await k8sProtocol.createPortForward({ podName: portForwardPod.metadata.name, namespace: portForwardPod.metadata.namespace, localPort: 8081, remotePort: 80 }); const activeForwards = await k8sProtocol.listPortForwards(); expect(activeForwards).toBeInstanceOf(Array); const createdForward = activeForwards.find(pf => pf.id === portForward.id); expect(createdForward).toBeDefined(); // Cleanup await k8sProtocol.stopPortForward(portForward.id); }, 15000); test('should stop port forward', async () => { const portForward = await k8sProtocol.createPortForward({ podName: portForwardPod.metadata.name, namespace: portForwardPod.metadata.namespace, localPort: 8082, remotePort: 80 }); expect(portForward.status).toBe('active'); await k8sProtocol.stopPortForward(portForward.id); const activeForwards = await k8sProtocol.listPortForwards(); const stoppedForward = activeForwards.find(pf => pf.id === portForward.id); expect(stoppedForward).toBeUndefined(); }, 15000); }); describe('Service and Deployment Management', () => { let testDeployment: K8sDeployment; let testService: K8sService; const deploymentName = `test-deployment-${Date.now()}`; const serviceName = `test-service-${Date.now()}`; afterEach(async () => { if (testDeployment) { try { await k8sProtocol.deleteDeployment(deploymentName, 'default'); } catch (error) { // Ignore cleanup errors } } if (testService) { try { await k8sProtocol.deleteService(serviceName, 'default'); } catch (error) { // Ignore cleanup errors } } }); test('should create and manage deployment', async () => { testDeployment = await k8sProtocol.createDeployment({ name: deploymentName, namespace: 'default', image: 'nginx', replicas: 2, labels: { app: 'test-nginx' } }); expect(testDeployment.metadata.name).toBe(deploymentName); expect(testDeployment.spec.replicas).toBe(2); // Wait for deployment to be ready await k8sProtocol.waitForDeploymentReady(deploymentName, 'default', 60000); const deployments = await k8sProtocol.listDeployments('default'); const createdDeployment = deployments.find(d => d.metadata.name === deploymentName); expect(createdDeployment).toBeDefined(); }, 30000); test('should create and manage service', async () => { // First create deployment for service testDeployment = await k8sProtocol.createDeployment({ name: deploymentName, namespace: 'default', image: 'nginx', replicas: 1, labels: { app: 'test-nginx' } }); testService = await k8sProtocol.createService({ name: serviceName, namespace: 'default', selector: { app: 'test-nginx' }, ports: [{ port: 80, targetPort: 80 }], type: 'ClusterIP' }); expect(testService.metadata.name).toBe(serviceName); expect(testService.spec.type).toBe('ClusterIP'); const services = await k8sProtocol.listServices('default'); const createdService = services.find(s => s.metadata.name === serviceName); expect(createdService).toBeDefined(); }, 25000); test('should scale deployment', async () => { testDeployment = await k8sProtocol.createDeployment({ name: deploymentName, namespace: 'default', image: 'nginx', replicas: 1, labels: { app: 'test-nginx' } }); await k8sProtocol.scaleDeployment(deploymentName, 'default', 3); const scaledDeployment = await k8sProtocol.getDeployment(deploymentName, 'default'); expect(scaledDeployment.spec.replicas).toBe(3); }, 25000); }); describe('Security and RBAC', () => { test('should enforce namespace restrictions', async () => { await expect(k8sProtocol.listPods('restricted-namespace')).rejects.toThrow(); }); test('should restrict access to sensitive resources', async () => { const restrictedResources = mockConfig.security.restrictedResources; for (const resource of restrictedResources) { await expect(k8sProtocol.listResources(resource, 'default')).rejects.toThrow(); } }); test('should validate service account permissions', async () => { const permissions = await k8sProtocol.checkServiceAccountPermissions( mockConfig.security.serviceAccountName, 'default' ); expect(permissions).toBeDefined(); expect(permissions.canList).toBeDefined(); expect(permissions.canCreate).toBeDefined(); expect(permissions.canDelete).toBeDefined(); }); test('should audit kubectl operations', async () => { const auditSpy = jest.fn<any>(); k8sProtocol.on('kubectl-audit', auditSpy); await k8sProtocol.listPods('default'); expect(auditSpy).toHaveBeenCalled(); const auditEntry = auditSpy.mock.calls[0][0]; expect(auditEntry.operation).toBe('list'); expect(auditEntry.resource).toBe('pods'); expect(auditEntry.namespace).toBe('default'); expect(auditEntry.timestamp).toBeInstanceOf(Date); }); }); describe('Error Handling and Resilience', () => { test('should handle API server unavailability', async () => { // Stop the mock API server await testServerManager.stopServer('k8s-api-server'); await expect(k8sProtocol.listPods('default')).rejects.toThrow(); // Restart the server for other tests await testServerManager.startServer('k8s-api-server'); }); test('should retry failed operations', async () => { const retrySpy = jest.fn<any>(); k8sProtocol.on('operation-retry', retrySpy); // This should trigger retries due to server behavior try { await k8sProtocol.listPods('default'); } catch (error) { // May fail after retries } // Check if retries were attempted // (This depends on the mock server's error rate) }); test('should handle concurrent operations', async () => { const podPromises = Array.from({ length: 5 }, (_, i) => k8sProtocol.createPod({ name: `concurrent-pod-${i}-${Date.now()}`, namespace: 'default', image: 'busybox', command: ['sleep', '300'] }).catch(() => null) // Catch expected failures ); const pods = await Promise.all(podPromises); const successfulPods = pods.filter(p => p !== null); expect(successfulPods.length).toBeGreaterThan(0); // Cleanup await Promise.all(successfulPods.map(pod => k8sProtocol.deletePod(pod!.metadata.name, pod!.metadata.namespace).catch(() => {}) )); }, 30000); test('should handle resource quota exceeded', async () => { // Try to create a pod with excessive resource requests await expect(k8sProtocol.createPod({ name: `resource-exhaustion-${Date.now()}`, namespace: 'default', image: 'nginx', resources: { requests: { cpu: '1000', memory: '100Gi' }, limits: { cpu: '1000', memory: '100Gi' } } })).rejects.toThrow(); }); }); describe('Session Cleanup and Resource Management', () => { test('should cleanup exec sessions properly', async () => { const testPod = await k8sProtocol.createPod({ name: `cleanup-pod-${Date.now()}`, namespace: 'default', image: 'busybox', command: ['sleep', '300'] }); await k8sProtocol.waitForPodReady(testPod.metadata.name, testPod.metadata.namespace, 30000); const session = await k8sProtocol.createSession({ command: '/bin/sh', consoleType: 'kubernetes' as const, podName: testPod.metadata.name, namespace: testPod.metadata.namespace, containerName: 'main' }); expect(session.status).toBe('running'); await k8sProtocol.terminateSession(session.id); const terminatedSession = k8sProtocol.getSession(session.id); expect(terminatedSession?.status).toBe('terminated'); // Cleanup pod await k8sProtocol.deletePod(testPod.metadata.name, testPod.metadata.namespace); }, 25000); test('should cleanup all resources on protocol shutdown', async () => { const testPod1 = await k8sProtocol.createPod({ name: `cleanup1-${Date.now()}`, namespace: 'default', image: 'busybox', command: ['sleep', '300'] }); const testPod2 = await k8sProtocol.createPod({ name: `cleanup2-${Date.now()}`, namespace: 'default', image: 'busybox', command: ['sleep', '300'] }); await Promise.all([ k8sProtocol.waitForPodReady(testPod1.metadata.name, testPod1.metadata.namespace, 30000), k8sProtocol.waitForPodReady(testPod2.metadata.name, testPod2.metadata.namespace, 30000) ]); const session1 = await k8sProtocol.createSession({ command: '/bin/sh', consoleType: 'kubernetes' as const, podName: testPod1.metadata.name, namespace: testPod1.metadata.namespace, containerName: 'main' }); const session2 = await k8sProtocol.createSession({ command: '/bin/sh', consoleType: 'kubernetes' as const, podName: testPod2.metadata.name, namespace: testPod2.metadata.namespace, containerName: 'main' }); expect(k8sProtocol.getAllSessions().length).toBe(2); await k8sProtocol.cleanup(); expect(k8sProtocol.getAllSessions().length).toBe(0); // Cleanup pods manually since protocol cleanup doesn't delete pods await Promise.all([ k8sProtocol.deletePod(testPod1.metadata.name, testPod1.metadata.namespace).catch(() => {}), k8sProtocol.deletePod(testPod2.metadata.name, testPod2.metadata.namespace).catch(() => {}) ]); }, 35000); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ooples/mcp-console-automation'

If you have feedback or need assistance with the MCP directory API, please join our Discord server