Skip to main content
Glama
policy-operations.ts22.5 kB
/** * Core Policy Operations * * Handles policy intent management operations including CRUD operations * and Kyverno cluster policy cleanup */ import { ErrorHandler, ErrorCategory, ErrorSeverity, Logger } from './error-handling'; import { PolicyVectorService } from './policy-vector-service'; import { VectorDBService } from './vector-db-service'; import { UnifiedCreationSessionManager } from './unified-creation-session'; import { executeKubectl } from './kubernetes-utils'; import { maybeGetFeedbackMessage } from './index'; // Note: validateVectorDBConnection and validateEmbeddingService are shared utilities // that remain in the main organizational-data.ts file as they're used by multiple domains /** * Get initialized policy service */ export async function getPolicyService(): Promise<PolicyVectorService> { const policyService = new PolicyVectorService(); // Always ensure proper collection initialization try { await policyService.initialize(); } catch (error) { // If initialization fails, try to provide helpful error context const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Vector DB collection initialization failed: ${errorMessage}. This may be due to dimension mismatch or collection configuration issues.`); } return policyService; } /** * Find Kyverno policies by policy intent ID using label selector */ export async function findKyvernoPoliciesByPolicyId( policyId: string, logger: Logger, requestId: string ): Promise<any[]> { try { logger.info('Searching for Kyverno policies by policy ID', { requestId, policyId }); const output = await executeKubectl(['get', 'clusterpolicy', '-l', `policy-intent/id=${policyId}`, '-o', 'json'], { kubeconfig: process.env.KUBECONFIG, timeout: 15000 }); const parsedOutput = JSON.parse(output || '{"items": []}'); const policies = parsedOutput.items || []; logger.info('Found Kyverno policies for policy intent', { requestId, policyId, policyCount: policies.length, policyNames: policies.map((p: any) => p.metadata?.name) }); return policies.map((p: any) => ({ name: p.metadata?.name, labels: p.metadata?.labels, creationTimestamp: p.metadata?.creationTimestamp })); } catch (error) { logger.warn('Failed to query Kyverno policies (cluster may not have Kyverno or no policies found)', { requestId, policyId, error: error instanceof Error ? error.message : String(error) }); return []; } } /** * Find all Kyverno policies that have policy-intent/id labels */ export async function findAllKyvernoPoliciesForPolicyIntents( logger: Logger, requestId: string ): Promise<any[]> { try { logger.info('Searching for all Kyverno policies with policy-intent labels', { requestId }); const output = await executeKubectl(['get', 'clusterpolicy', '-l', 'policy-intent/id', '-o', 'json'], { kubeconfig: process.env.KUBECONFIG, timeout: 15000 }); const parsedOutput = JSON.parse(output || '{"items": []}'); const policies = parsedOutput.items || []; logger.info('Found all Kyverno policies for policy intents', { requestId, policyCount: policies.length, policyNames: policies.map((p: any) => p.metadata?.name) }); return policies.map((p: any) => ({ name: p.metadata?.name, policyId: p.metadata?.labels?.['policy-intent/id'], labels: p.metadata?.labels, creationTimestamp: p.metadata?.creationTimestamp })); } catch (error) { logger.warn('Failed to query all Kyverno policies (cluster may not have Kyverno or no policies found)', { requestId, error: error instanceof Error ? error.message : String(error) }); return []; } } /** * Delete Kyverno policies by policy intent ID using label selector */ export async function deleteKyvernoPoliciesByPolicyId( policyId: string, logger: Logger, requestId: string ): Promise<any> { try { logger.info('Deleting Kyverno policies by policy ID', { requestId, policyId }); const output = await executeKubectl(['delete', 'clusterpolicy', '-l', `policy-intent/id=${policyId}`], { kubeconfig: process.env.KUBECONFIG, timeout: 30000 }); logger.info('Kyverno policies deleted successfully', { requestId, policyId, output }); return { successful: [{ policyId, deletedAt: new Date().toISOString() }], failed: [], total: 1 }; } catch (error) { logger.error('Failed to delete Kyverno policies', error as Error, { requestId, policyId, error: error instanceof Error ? error.message : String(error) }); return { successful: [], failed: [{ policyId, error: error instanceof Error ? error.message : String(error) }], total: 1 }; } } /** * Delete all Kyverno policies that have policy-intent/id labels */ export async function deleteAllKyvernoPoliciesForPolicyIntents( logger: Logger, requestId: string ): Promise<any> { try { logger.info('Deleting all Kyverno policies with policy-intent labels', { requestId }); const output = await executeKubectl(['delete', 'clusterpolicy', '-l', 'policy-intent/id'], { kubeconfig: process.env.KUBECONFIG, timeout: 30000 }); logger.info('All Kyverno policies deleted successfully', { requestId, output }); return { successful: [{ deletedAt: new Date().toISOString() }], failed: [], total: 1 }; } catch (error) { logger.error('Failed to delete all Kyverno policies', error as Error, { requestId, error: error instanceof Error ? error.message : String(error) }); return { successful: [], failed: [{ error: error instanceof Error ? error.message : String(error) }], total: 1 }; } } /** * Handle individual policy delete with Kyverno cleanup */ export async function handlePolicyDelete( policyId: string, policyService: any, args: any, logger: Logger, requestId: string ): Promise<any> { try { // Check if policy intent exists const existingPolicyIntent = await policyService.getPolicyIntent(policyId); if (!existingPolicyIntent) { return { success: false, operation: 'delete', dataType: 'policy', message: `Policy intent not found: ${policyId}`, error: 'Policy intent not found' }; } // Check if there are deployed Kyverno policies with this policy ID const kyvernoPolicies = await findKyvernoPoliciesByPolicyId(policyId, logger, requestId); if (kyvernoPolicies.length > 0 && !args.response) { // Show confirmation prompt for Kyverno cleanup return { success: true, operation: 'delete', dataType: 'policy', requiresConfirmation: true, message: 'Policy intent has deployed Kyverno policies that need cleanup decision', confirmation: { question: `Policy intent "${existingPolicyIntent.description.substring(0, 60)}..." has ${kyvernoPolicies.length} deployed Kyverno policies in your cluster: ${kyvernoPolicies.map(p => p.name).join(', ')}\n\n**Choose what to do:**\n\n1. **Delete everything** - Remove policy intent AND delete Kyverno policies from cluster\n2. **Keep Kyverno policies** - Remove policy intent only, preserve cluster policies\n\n⚠️ **Warning**: Option 1 will remove active policy enforcement from your cluster.\n\n**What would you like to do?**`, options: ['Delete everything', 'Keep Kyverno policies'] }, policyIntent: existingPolicyIntent, kyvernoPolicies: kyvernoPolicies }; } // Process user's response or proceed with direct deletion let kyvernoCleanupResults = null; if (kyvernoPolicies.length > 0 && args.response) { const response = args.response.trim(); if (response === '1' || response.toLowerCase().includes('delete everything')) { // Delete Kyverno policies from cluster kyvernoCleanupResults = await deleteKyvernoPoliciesByPolicyId(policyId, logger, requestId); } } // Always delete the policy intent from Vector DB await policyService.deletePolicyIntent(policyId); const cleanupMessage = kyvernoCleanupResults ? `with Kyverno cleanup (${kyvernoCleanupResults.successful.length} deleted, ${kyvernoCleanupResults.failed.length} failed)` : kyvernoPolicies.length > 0 ? '(Kyverno policies preserved in cluster)' : '(no Kyverno policies to cleanup)'; return { success: true, operation: 'delete', dataType: 'policy', message: `Policy intent deleted successfully ${cleanupMessage}`, deletedPolicyIntent: existingPolicyIntent, kyvernoCleanup: kyvernoCleanupResults || { preserved: true } }; } catch (error) { logger.error('Failed to delete policy intent', error as Error, { requestId, policyId }); return { success: false, operation: 'delete', dataType: 'policy', message: 'Failed to delete policy intent', error: error instanceof Error ? error.message : String(error) }; } } /** * Handle deleteAll policies with batch Kyverno cleanup */ export async function handlePolicyDeleteAll( policyService: any, args: any, logger: Logger, requestId: string ): Promise<any> { try { // Get all policy intents const allPolicyIntents = await policyService.getAllPolicyIntents(); if (!allPolicyIntents || allPolicyIntents.length === 0) { return { success: true, operation: 'deleteAll', dataType: 'policy', message: 'No policy intents found to delete', deletedCount: 0 }; } // Find all deployed Kyverno policies for all policy intents const allKyvernoPolicies = await findAllKyvernoPoliciesForPolicyIntents(logger, requestId); if (allKyvernoPolicies.length > 0 && !args.response) { // Show confirmation prompt for batch Kyverno cleanup return { success: true, operation: 'deleteAll', dataType: 'policy', requiresConfirmation: true, message: 'Found policy intents with deployed Kyverno policies that need cleanup decision', confirmation: { question: `Deleting ${allPolicyIntents.length} policy intents. Found ${allKyvernoPolicies.length} deployed Kyverno policies in your cluster: ${allKyvernoPolicies.map(p => p.name).join(', ')}\n\n**Choose what to do:**\n\n1. **Delete everything** - Remove all policy intents AND delete all Kyverno policies from cluster\n2. **Keep Kyverno policies** - Remove all policy intents only, preserve all cluster policies\n\n⚠️ **Warning**: Option 1 will remove ALL active policy enforcement from your cluster.\n\n**What would you like to do?**`, options: ['Delete everything', 'Keep Kyverno policies'] }, policyIntents: allPolicyIntents, kyvernoPolicies: allKyvernoPolicies }; } // Process user's response or proceed with direct deletion let kyvernoCleanupResults = null; if (allKyvernoPolicies.length > 0 && args.response) { const response = args.response.trim(); if (response === '1' || response.toLowerCase().includes('delete everything')) { // Delete all Kyverno policies from cluster kyvernoCleanupResults = await deleteAllKyvernoPoliciesForPolicyIntents(logger, requestId); } } // Always delete all policy intents from Vector DB for (const policyIntent of allPolicyIntents) { await policyService.deletePolicyIntent(policyIntent.id); } const cleanupMessage = kyvernoCleanupResults ? `with Kyverno cleanup (${kyvernoCleanupResults.successful.length} deleted, ${kyvernoCleanupResults.failed.length} failed)` : allKyvernoPolicies.length > 0 ? '(Kyverno policies preserved in cluster)' : '(no Kyverno policies to cleanup)'; return { success: true, operation: 'deleteAll', dataType: 'policy', message: `All ${allPolicyIntents.length} policy intents deleted successfully ${cleanupMessage}`, deletedCount: allPolicyIntents.length, deletedPolicyIntents: allPolicyIntents, kyvernoCleanup: kyvernoCleanupResults || { preserved: true } }; } catch (error) { logger.error('Failed to delete all policy intents', error as Error, { requestId }); return { success: false, operation: 'deleteAll', dataType: 'policy', message: 'Failed to delete all policy intents', error: error instanceof Error ? error.message : String(error) }; } } /** * Main policy operations handler - delegates to specific operation functions * Requires shared validation utilities to be passed as parameters to avoid circular imports */ export async function handlePolicyOperation( operation: string, args: any, logger: Logger, requestId: string, validateVectorDBConnection: (vectorService: PolicyVectorService, logger: Logger, requestId: string) => Promise<{ success: boolean; error?: any }>, validateEmbeddingService: (logger: Logger, requestId: string) => Promise<{ success: boolean; error?: any }> ): Promise<any> { // Get policy service and validate Vector DB connection const policyService = await getPolicyService(); const connectionCheck = await validateVectorDBConnection(policyService, logger, requestId); if (!connectionCheck.success) { return { success: false, operation, dataType: 'policy', error: connectionCheck.error, message: 'Vector DB connection required for policy management' }; } // Validate embedding service and fail if unavailable (except for delete operations) const operationsRequiringEmbedding = ['create', 'search']; if (operationsRequiringEmbedding.includes(operation)) { const embeddingCheck = await validateEmbeddingService(logger, requestId); if (!embeddingCheck.success) { return { success: false, operation, dataType: 'policy', error: embeddingCheck.error, message: 'OpenAI API key required for policy management' }; } } const sessionManager = new UnifiedCreationSessionManager('policy'); switch (operation) { case 'create': { let workflowStep; if (args.sessionId) { // Continue existing session logger.info('Continuing policy creation workflow', { requestId, sessionId: args.sessionId }); if (args.response) { // Process user response and move to next step const updatedSession = sessionManager.processResponse(args.sessionId, args.response, args); workflowStep = await sessionManager.getNextWorkflowStep(updatedSession, args); } else { // Just get current step without processing response const session = sessionManager.loadSession(args.sessionId, args); if (!session) { throw ErrorHandler.createError( ErrorCategory.VALIDATION, ErrorSeverity.HIGH, `Session not found: ${args.sessionId}`, { operation: 'policy_workflow_continue', component: 'OrganizationalDataTool', requestId, input: { sessionId: args.sessionId } } ); } workflowStep = await sessionManager.getNextWorkflowStep(session, args); } if (!workflowStep) { throw ErrorHandler.createError( ErrorCategory.VALIDATION, ErrorSeverity.HIGH, `Session not found or workflow failed`, { operation: 'policy_workflow_continue', component: 'OrganizationalDataTool', requestId, input: { sessionId: args.sessionId } } ); } } else { // Start new workflow session logger.info('Starting new policy creation workflow', { requestId }); const session = sessionManager.createSession(args); workflowStep = await sessionManager.getNextWorkflowStep(session, args); if (!workflowStep) { throw ErrorHandler.createError( ErrorCategory.OPERATION, ErrorSeverity.HIGH, 'Failed to initialize policy creation workflow', { operation: 'policy_workflow_start', component: 'OrganizationalDataTool', requestId } ); } } // Always check if workflow is complete and store policy in Vector DB let storageInfo: any = {}; const isComplete = !('nextStep' in workflowStep) || !workflowStep.nextStep; // Complete when no next step const hasPolicy = !!workflowStep.data?.policy; logger.info('Checking workflow completion', { requestId, nextStep: ('nextStep' in workflowStep) ? workflowStep.nextStep : 'complete', hasPolicy, policyId: workflowStep.data?.policy?.id }); if (isComplete && hasPolicy) { try { await policyService.storePolicyIntent(workflowStep.data.policy); const vectorDBConfig = new VectorDBService({ collectionName: 'policies' }).getConfig(); storageInfo = { stored: true, vectorDbUrl: vectorDBConfig.url, collectionName: vectorDBConfig.collectionName, policyId: workflowStep.data.policy.id }; logger.info('Policy stored in Vector DB successfully', { requestId, policyId: workflowStep.data.policy.id, description: workflowStep.data.policy.description.substring(0, 50) + (workflowStep.data.policy.description.length > 50 ? '...' : '') }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); storageInfo = { stored: false, error: errorMessage, policyId: workflowStep.data.policy.id }; logger.error('Failed to store policy in Vector DB', error instanceof Error ? error : new Error(String(error)), { requestId, policyId: workflowStep.data.policy.id }); } } // For completed policies, storage failure means creation failure const storageSucceeded = storageInfo.stored === true; const operationSucceeded = !isComplete || storageSucceeded; // Check if we should show feedback message (workflow completion point) const feedbackMessage = (isComplete && storageSucceeded) ? maybeGetFeedbackMessage() : ''; return { success: operationSucceeded, operation: 'create', dataType: 'policy', workflow: workflowStep, storage: storageInfo, message: isComplete ? (storageSucceeded ? `Policy created and stored successfully${feedbackMessage}` : 'Policy creation failed - storage error') : 'Workflow step ready' }; } case 'list': { const limit = args.limit || 10; const policyIntents = await policyService.getAllPolicyIntents(); const totalCount = await policyService.getPolicyIntentsCount(); const limitedPolicyIntents = policyIntents.slice(0, limit); return { success: true, operation, dataType: 'policy', message: `Found ${totalCount} policy intents (showing ${limitedPolicyIntents.length})`, policyIntents: limitedPolicyIntents, totalCount, note: totalCount > limit ? `Showing first ${limit} of ${totalCount} policy intents. Use limit parameter to see more.` : undefined }; } case 'get': { if (!args.id) { return { success: false, operation, dataType: 'policy', message: 'Policy intent ID is required for get operation', error: 'Missing required parameter: id' }; } const policyIntent = await policyService.getPolicyIntent(args.id); if (!policyIntent) { return { success: false, operation, dataType: 'policy', message: `Policy intent not found: ${args.id}`, error: 'Policy intent not found' }; } return { success: true, operation, dataType: 'policy', message: 'Policy intent retrieved successfully', policyIntent }; } case 'search': { if (!args.id) { // For search, 'id' parameter contains the search query return { success: false, operation, dataType: 'policy', message: 'Search query is required (use id parameter)', error: 'Missing required parameter: id (search query)' }; } const limit = args.limit || 10; const searchResults = await policyService.searchPolicyIntents(args.id, { limit }); return { success: true, operation, dataType: 'policy', message: `Found ${searchResults.length} policy intents matching "${args.id}"`, policyIntents: searchResults.map(result => result.data), searchResults: searchResults.map(result => ({ policyIntent: result.data, score: result.score })) }; } case 'delete': { if (!args.id) { return { success: false, operation, dataType: 'policy', message: 'Policy intent ID is required for delete operation', error: 'Missing required parameter: id' }; } return await handlePolicyDelete(args.id, policyService, args, logger, requestId); } case 'deleteAll': { return await handlePolicyDeleteAll(policyService, args, logger, requestId); } default: return { success: false, operation, dataType: 'policy', message: `Unsupported operation: ${operation}. Supported operations: create, list, get, search, delete, deleteAll`, error: 'Unsupported operation' }; } }

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/vfarcic/dot-ai'

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