1+ /**
2+ * k8s-client.js
3+ * Zuständig für Kubernetes Kontext-Informationen und Istio-Netzwerkdiagnose.
4+ */
5+ const K8sClient = ( ( ) => {
6+
7+ // Private Hilfsfunktion für API-Anfragen an den DiagnosticController
8+ async function apiFetch ( endpoint ) {
9+ const response = await fetch ( endpoint ) ;
10+ if ( ! response . ok ) {
11+ throw new Error ( `Fehler beim Abrufen der K8s-Daten (Status: ${ response . status } )` ) ;
12+ }
13+ return await response . json ( ) ;
14+ }
15+
16+ /**
17+ * Erstellt das HTML für die Anzeige der Istio-Ressourcen
18+ */
19+ function renderDiagnostics ( container , data ) {
20+ let html = `
21+ <div class="row g-3">
22+ <div class="col-md-6">
23+ <div class="p-2 border rounded bg-light h-100">
24+ <h6 class="fw-bold border-bottom pb-2">
25+ <i class="bi bi-shuffle me-2"></i>Virtual Services
26+ <span class="badge bg-secondary float-end">${ data . virtualServices . length } </span>
27+ </h6>
28+ <div class="list-group list-group-flush shadow-sm mt-2">
29+ ${ data . virtualServices . map ( v => `
30+ <div class="list-group-item p-2 small">
31+ <span class="text-primary fw-bold">${ v . metadata . name } </span>
32+ </div>
33+ ` ) . join ( '' ) || '<div class="text-muted small p-2 italic">Keine VirtualServices gefunden</div>' }
34+ </div>
35+ </div>
36+ </div>
37+ <div class="col-md-6">
38+ <div class="p-2 border rounded bg-light h-100">
39+ <h6 class="fw-bold border-bottom pb-2">
40+ <i class="bi bi-shield-check me-2"></i>Destination Rules
41+ <span class="badge bg-secondary float-end">${ data . destinationRules . length } </span>
42+ </h6>
43+ <div class="list-group list-group-flush shadow-sm mt-2">
44+ ${ data . destinationRules . map ( d => `
45+ <div class="list-group-item p-2 small">
46+ <span class="text-success fw-bold">${ d . metadata . name } </span>
47+ </div>
48+ ` ) . join ( '' ) || '<div class="text-muted small p-2 italic">Keine DestinationRules gefunden</div>' }
49+ </div>
50+ </div>
51+ </div>
52+ </div>
53+ <div class="mt-3 p-2 bg-dark text-white rounded x-small">
54+ <i class="bi bi-info-circle me-2"></i>Gefiltert nach Host: <strong>${ data . host } </strong> im Namespace: <strong>${ data . namespace } </strong>
55+ </div>
56+ ` ;
57+ container . innerHTML = html ;
58+ }
59+
60+ return {
61+ // Lädt die Pod- und Namespace-Informationen für die Navbar
62+ loadContext : async ( ) => {
63+ try {
64+ return await apiFetch ( '/api/k8s/context' ) ;
65+ } catch ( err ) {
66+ console . error ( "K8s Context Error:" , err ) ;
67+ return { error : err . message } ;
68+ }
69+ } ,
70+
71+ // Führt eine Tiefen-Diagnose für eine Ziel-URL durch
72+ runFullDiagnostics : async ( targetUrl ) => {
73+ const contentDiv = document . getElementById ( 'istioContent' ) ;
74+ if ( ! contentDiv ) return ;
75+
76+ contentDiv . innerHTML = `
77+ <div class="text-center p-4">
78+ <div class="spinner-border text-info" role="status"></div>
79+ <div class="mt-2">Analysiere Routing-Regeln im Cluster...</div>
80+ </div>` ;
81+
82+ try {
83+ // Namespace aus URL extrahieren (Erwartet Format: http://service.namespace.svc...)
84+ const urlObj = new URL ( targetUrl ) ;
85+ const host = urlObj . hostname ;
86+ const parts = host . split ( '.' ) ;
87+
88+ // Logik: service.namespace.svc -> namespace ist an Index 1
89+ // Bei "localhost" oder einfachen Namen nehmen wir "default"
90+ const namespace = ( parts . length > 1 && parts [ 1 ] !== 'svc' ) ? parts [ 1 ] : 'default' ;
91+
92+ const [ vs , dr ] = await Promise . all ( [
93+ apiFetch ( `/api/k8s/istio/virtualservice?namespace=${ namespace } ` ) ,
94+ apiFetch ( `/api/k8s/istio/destinationrule?namespace=${ namespace } ` )
95+ ] ) ;
96+
97+ // Filtern der Ressourcen, die den Host-Namen im Spec oder Metadaten enthalten
98+ const virtualServices = vs . filter ( item => JSON . stringify ( item ) . includes ( parts [ 0 ] ) ) ;
99+ const destinationRules = dr . filter ( item => JSON . stringify ( item ) . includes ( parts [ 0 ] ) ) ;
100+
101+ renderDiagnostics ( contentDiv , { host, namespace, virtualServices, destinationRules } ) ;
102+ } catch ( err ) {
103+ contentDiv . innerHTML = `
104+ <div class="alert alert-warning">
105+ <i class="bi bi-exclamation-triangle me-2"></i>
106+ <strong>Diagnose eingeschränkt:</strong><br>
107+ ${ err . message } . Stellen Sie sicher, dass es sich um eine interne Cluster-URL handelt.
108+ </div>` ;
109+ }
110+ }
111+ } ;
112+ } ) ( ) ;
113+
114+ // Initialisierung bei DOM-Ready
115+ document . addEventListener ( 'DOMContentLoaded' , async ( ) => {
116+ const contextEl = document . getElementById ( 'k8sContext' ) ;
117+ const diagnoseBtn = document . getElementById ( 'k8sDiagnoseBtn' ) ;
118+ const istioPanel = document . getElementById ( 'istioPanel' ) ;
119+
120+ // 1. Kontext laden (Navbar)
121+ const ctx = await K8sClient . loadContext ( ) ;
122+ if ( contextEl ) {
123+ if ( ctx . error ) {
124+ contextEl . innerHTML = `<span class="badge bg-danger">K8s Offline</span>` ;
125+ } else {
126+ contextEl . innerHTML = `
127+ <span class="badge bg-opacity-25 bg-light border me-2" title="Aktueller Namespace">
128+ <i class="bi bi-tags me-1"></i>${ ctx . namespace }
129+ </span>
130+ <span class="badge ${ ctx . istioSidecar ? 'bg-success' : 'bg-warning text-dark' } " title="Istio Sidecar Status">
131+ <i class="bi bi-shield-check me-1"></i>Istio: ${ ctx . istioSidecar ? 'ON' : 'OFF' }
132+ </span>
133+ ` ;
134+ }
135+ }
136+
137+ // 2. Event Listener für den Diagnose-Button
138+ if ( diagnoseBtn ) {
139+ diagnoseBtn . addEventListener ( 'click' , ( ) => {
140+ const urlInput = document . getElementById ( 'url' ) . value ;
141+ if ( ! urlInput ) {
142+ alert ( "Bitte geben Sie erst eine Ziel-URL ein." ) ;
143+ return ;
144+ }
145+ if ( istioPanel ) istioPanel . style . display = 'block' ;
146+ K8sClient . runFullDiagnostics ( urlInput ) ;
147+ } ) ;
148+ }
149+ } ) ;
0 commit comments