1+ /**
2+ * k8s-client.js
3+ * Umfassende Diagnose für Envoy-Sidecars und Istio-Netzwerkressourcen.
4+ */
15const K8sClient = ( ( ) => {
26
37 async function apiFetch ( endpoint ) {
@@ -6,7 +10,9 @@ const K8sClient = (() => {
610 return await response . json ( ) ;
711 }
812
9- // Zeigt einfach alle Items an, die übergeben werden
13+ /**
14+ * Hilfsfunktion zum Rendern einer Ressourcen-Gruppe (VS, DR, oder GW)
15+ */
1016 function renderResourceGroup ( title , items , icon , colorClass ) {
1117 return `
1218 <div class="mb-3">
@@ -37,56 +43,58 @@ const K8sClient = (() => {
3743 const resourceDiv = document . getElementById ( 'resourceDisplay' ) ;
3844
3945 const spinner = '<div class="text-center p-4"><div class="spinner-border text-info"></div></div>' ;
40- [ configDiv , errorDiv , resourceDiv ] . forEach ( el => { if ( el ) el . innerHTML = spinner ; } ) ;
46+ [ configDiv , errorDiv , resourceDiv ] . forEach ( el => { if ( el ) el . innerHTML = spinner ; } ) ;
4147
4248 try {
43- // Namespace-Logik: Wir nutzen primär den Namespace, in dem die App selbst läuft,
44- // außer die URL ist voll qualifiziert (z.B. service.other-ns.svc).
4549 const urlObj = new URL ( targetUrl ) ;
4650 const hostParts = urlObj . hostname . split ( '.' ) ;
51+ const targetNamespace = ( hostParts . length > 1 && hostParts [ 1 ] !== 'svc' ) ? hostParts [ 1 ] : 'default' ;
4752
48- // Wir holen uns erst den eigenen Namespace der App aus dem Context
49- const context = await apiFetch ( '/api/k8s/context' ) ;
50- const currentNs = context . namespace || 'default' ;
51-
52- // Wenn die URL einen Namespace enthält (part[1]), nutze diesen, sonst den aktuellen.
53- const targetNamespace = ( hostParts . length > 1 && hostParts [ 1 ] !== 'svc' && hostParts [ 1 ] !== 'cluster' )
54- ? hostParts [ 1 ] : currentNs ;
55-
53+ // 1. Alle Daten parallel abrufen
5654 const [ report , vs , dr , gw ] = await Promise . all ( [
5755 apiFetch ( '/api/k8s/istio/full-report' ) ,
5856 apiFetch ( `/api/k8s/istio/virtualservice?namespace=${ targetNamespace } ` ) ,
5957 apiFetch ( `/api/k8s/istio/destinationrule?namespace=${ targetNamespace } ` ) ,
6058 apiFetch ( `/api/k8s/istio/gateway?namespace=${ targetNamespace } ` )
6159 ] ) ;
6260
63- // Render Sektionen
61+ if ( report . error ) throw new Error ( report . error ) ;
62+
63+ // --- TAB A: ENVOY CONFIG ---
6464 configDiv . innerHTML = `
6565 <div class="mb-3">
66- <label class="fw-bold small text-muted">ENVOY CLUSTERS:</label>
67- <pre class="console x-small" style="max-height: 250px; overflow:auto; background: #1a1a1a; color: #00ff41; padding: 12px;">${ report . reachability . activeEndpoints } </pre>
66+ <label class="fw-bold small text-muted">AKTIVE CLUSTER (UPSTREAM):</label>
67+ <pre class="console x-small" style="max-height: 250px; overflow:auto; background: #1a1a1a; color: #00ff41; padding: 12px; border: 1px solid #333;">${ report . reachability . activeEndpoints } </pre>
68+ <div class="badge bg-primary mt-1">${ report . reachability . summary } </div>
6869 </div>
69- <button class="btn btn-xs btn-outline-secondary" onclick="this.nextElementSibling.classList.toggle('d-none')">Raw JSON</button>
70- <pre class="console x-small d-none mt-2">${ JSON . stringify ( report . reachability . envoyConfig , null , 2 ) } </pre>` ;
70+ <button class="btn btn-xs btn-outline-secondary" onclick="this.nextElementSibling.classList.toggle('d-none')">Raw JSON Config </button>
71+ <pre class="console x-small d-none mt-2" style="max-height:300px; overflow:auto;" >${ JSON . stringify ( report . reachability . envoyConfig , null , 2 ) } </pre>` ;
7172
73+ // --- TAB B: FEHLER ---
7274 const errorEntries = Object . entries ( report . healthDiagnostics . activeErrorMetrics ) ;
73- errorDiv . innerHTML = errorEntries . length === 0 ?
74- `<div class="alert alert-success mt-2 small">Keine Fehler-Metriken > 0.</div>` :
75- `<table class="table table-sm table-hover small mt-2">
76- <thead class="table-dark"><tr><th>Metrik</th><th class="text-end">Wert</th></tr></thead>
77- <tbody>${ errorEntries . map ( ( [ k , v ] ) => `<tr><td class="x-small">${ k } </td><td class="text-end fw-bold text-danger">${ v } </td></tr>` ) . join ( '' ) } </tbody>
78- </table>` ;
79-
80- // Ressourcen ohne Filter anzeigen
75+ if ( errorEntries . length === 0 ) {
76+ errorDiv . innerHTML = `<div class="alert alert-success mt-2 small"><i class="bi bi-check-circle me-2"></i>Keine aktiven Fehlermetriken im Sidecar.</div>` ;
77+ } else {
78+ errorDiv . innerHTML = `
79+ <table class="table table-sm table-hover border small mt-2">
80+ <thead class="table-dark"><tr><th>Envoy Metrik</th><th class="text-end">Wert</th></tr></thead>
81+ <tbody>
82+ ${ errorEntries . map ( ( [ k , v ] ) => `<tr><td class="x-small font-monospace">${ k } </td><td class="text-end text-danger fw-bold">${ v } </td></tr>` ) . join ( '' ) }
83+ </tbody>
84+ </table>` ;
85+ }
86+
87+ // --- TAB C: ALLE K8S/ISTIO RESSOURCEN ---
8188 resourceDiv . innerHTML = `
82- <div class="alert alert-info py-1 px-2 x-small mb-3">Zeige Ressourcen im Namespace: <strong>${ targetNamespace } </strong></div>
83- ${ renderResourceGroup ( "Virtual Services" , vs , "bi-shuffle" , "border-primary" ) }
84- ${ renderResourceGroup ( "Destination Rules" , dr , "bi-shield-shaded" , "border-success" ) }
85- ${ renderResourceGroup ( "Gateways" , gw , "bi-door-open" , "border-warning" ) } ` ;
89+ <div class="p-1">
90+ ${ renderResourceGroup ( "Virtual Services" , vs , "bi-shuffle" , "border-primary" ) }
91+ ${ renderResourceGroup ( "Destination Rules" , dr , "bi-shield-shaded" , "border-success" ) }
92+ ${ renderResourceGroup ( "Gateways" , gw , "bi-door-open" , "border-warning" ) }
93+ </div>` ;
8694
8795 } catch ( err ) {
88- const msg = `<div class="alert alert-danger m-2 small">Fehler : ${ err . message } </div>` ;
89- [ configDiv , errorDiv , resourceDiv ] . forEach ( el => { if ( el ) el . innerHTML = msg ; } ) ;
96+ const msg = `<div class="alert alert-danger m-2 small">Diagnose fehlgeschlagen : ${ err . message } </div>` ;
97+ [ configDiv , errorDiv , resourceDiv ] . forEach ( el => { if ( el ) el . innerHTML = msg ; } ) ;
9098 }
9199 }
92100 } ;
@@ -108,12 +116,15 @@ document.addEventListener('DOMContentLoaded', async () => {
108116
109117 if ( diagnoseBtn ) {
110118 diagnoseBtn . addEventListener ( 'click' , ( ) => {
119+ const urlVal = document . getElementById ( 'url' ) . value ;
120+ if ( ! urlVal ) return alert ( "Bitte URL eingeben" ) ;
111121
112122 document . getElementById ( 'resultArea' ) . style . display = 'block' ;
113123 document . getElementById ( 'istioPanel' ) . style . display = 'block' ;
114124
125+ // Sicherer Tab-Wechsel ohne 'bootstrap is not defined' Risiko
115126 const firstTab = document . querySelector ( '#config-tab' ) ;
116- if ( firstTab ) firstTab . click ( ) ;
127+ if ( firstTab ) firstTab . click ( ) ;
117128
118129 K8sClient . runFullDiagnostics ( urlVal ) ;
119130 } ) ;
0 commit comments