UNPKG

@kinvolk/headlamp-plugin

Version:

The needed infrastructure for building Headlamp plugins.

215 lines (192 loc) 7.08 kB
import YAML from 'yaml'; // Function to validate and parse YAML to extract resource type and metadata export function parseKubernetesYAML(yamlStr: string): { isValid: boolean; resourceType?: string; name?: string; namespace?: string; } { try { const parsed = YAML.parse(yamlStr); if (!parsed || typeof parsed !== 'object') { return { isValid: false }; } // Check if it has the basic Kubernetes resource structure - just need apiVersion and kind if (!parsed.apiVersion || !parsed.kind) { return { isValid: false }; } // Extract resource name and namespace if available const name = parsed.metadata?.name; const namespace = parsed.metadata?.namespace || 'default'; return { isValid: true, resourceType: parsed.kind, name, namespace, }; } catch (error: any) { // Handle multi-document YAML (contains --- separators) // Parse only the first document and validate it if ( error?.message?.includes('multiple documents') || yamlStr.includes('\n---\n') || yamlStr.includes('\n---') ) { try { const docs = YAML.parseAllDocuments(yamlStr); if (docs.length > 0) { const firstDoc = docs[0].toJSON(); if (firstDoc && typeof firstDoc === 'object' && firstDoc.apiVersion && firstDoc.kind) { const name = firstDoc.metadata?.name; const namespace = firstDoc.metadata?.namespace || 'default'; return { isValid: true, resourceType: firstDoc.kind, name, namespace, }; } } } catch (multiDocError) { console.warn('Failed to parse multi-document YAML:', multiDocError); } } console.warn('Failed to parse YAML:', error); return { isValid: false }; } } // Extract YAML content from text that may contain markdown-style code blocks or separator patterns export function extractYamlContent( content: string ): { yaml: string; resourceType: string; title?: string }[] | null { if (!content) return null; const results: { yaml: string; resourceType: string; title?: string }[] = []; // Simple YAML extraction: // 1. Split content by triple backticks const codeBlockRegex = /```yaml\n([\s\S]*?)```/g; const codeBlockRegex2 = /```([\s\S]*?)```/g; const codeBlockRegex3 = /~~~yaml\n([\s\S]*?)~~~(?!\n)/g; const codeBlockRegex4 = /~~~([\s\S]*?)~~~(?!\n)/g; const extractFromCodeBlock = (match: string) => { const yamlContent = match.trim(); try { const parsed = YAML.parse(yamlContent); if (parsed && typeof parsed === 'object' && parsed.apiVersion && parsed.kind) { results.push({ yaml: yamlContent, resourceType: parsed.kind, }); } } catch (e) {} }; let match; while ((match = codeBlockRegex.exec(content)) !== null) { extractFromCodeBlock(match[1]); } while ((match = codeBlockRegex2.exec(content)) !== null) { extractFromCodeBlock(match[1]); } while ((match = codeBlockRegex3.exec(content)) !== null) { extractFromCodeBlock(match[1]); } while ((match = codeBlockRegex4.exec(content)) !== null) { extractFromCodeBlock(match[1]); } // If no results found with code blocks, fall back to other patterns if (results.length === 0) { // Look for YAML surrounded by dashed lines const dashedPattern = /[-]{3,}[\r\n]+([\s\S]*?)[-]{3,}/g; let dashMatch; while ((dashMatch = dashedPattern.exec(content)) !== null) { if (dashMatch[1] && dashMatch[1].trim()) { const yamlContent = dashMatch[1].trim(); try { const parsed = YAML.parse(yamlContent); if (parsed && typeof parsed === 'object' && parsed.apiVersion && parsed.kind) { // Try to find a title from the preceding content const precedingContent = content.substring(0, dashMatch.index).trim(); const lastLineBreak = precedingContent.lastIndexOf('\n'); const possibleTitle = precedingContent.substring(lastLineBreak + 1).trim(); results.push({ yaml: yamlContent, resourceType: parsed.kind, title: possibleTitle.length > 0 ? possibleTitle : `Sample ${parsed.kind}`, }); } } catch (e) {} } } // If still no results, fall back to other extraction methods if (results.length === 0) { // Check for YAML code blocks using markdown style const yamlRegex = /```(?:ya?ml)?\s*([\s\S]*?)```/g; let match; while ((match = yamlRegex.exec(content)) !== null) { if (match[1] && match[1].trim()) { const yamlContent = match[1].trim(); try { const parsed = YAML.parse(yamlContent); if (parsed && typeof parsed === 'object' && parsed.apiVersion && parsed.kind) { results.push({ yaml: yamlContent, resourceType: parsed.kind, }); } } catch (e) { console.log('Error parsing YAML code block:', e); } } } // If still no results, look for sections with clear apiVersion and kind if (results.length === 0) { const contentByLines = content.split('\n'); let currentYaml = ''; let inYamlBlock = false; for (let i = 0; i < contentByLines.length; i++) { const line = contentByLines[i].trim(); if (line.startsWith('apiVersion:')) { // Start collecting a new YAML block inYamlBlock = true; currentYaml = line + '\n'; } else if (inYamlBlock) { // If we hit a blank line after collecting some YAML, try to finalize it if (line === '' && currentYaml.length > 0) { try { const parsed = YAML.parse(currentYaml); if (parsed && typeof parsed === 'object' && parsed.apiVersion && parsed.kind) { results.push({ yaml: currentYaml.trim(), resourceType: parsed.kind, }); } } catch (e) { // Not valid YAML yet, continue collecting } // Reset for next block currentYaml = ''; inYamlBlock = false; } else { // Keep collecting lines currentYaml += line + '\n'; } } } // Check the last collected block if (inYamlBlock && currentYaml.length > 0) { try { const parsed = YAML.parse(currentYaml); if (parsed && typeof parsed === 'object' && parsed.apiVersion && parsed.kind) { results.push({ yaml: currentYaml.trim(), resourceType: parsed.kind, }); } } catch (e) { // Not valid YAML, ignore } } } } } return results.length > 0 ? results : null; }