Skip to content

Commit c036e6a

Browse files
author
Eugenio Marzo
committed
some fixes
1 parent 6cb6ce2 commit c036e6a

6 files changed

Lines changed: 307 additions & 45 deletions

File tree

html/index.html

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
<link rel="icon" type="image/x-icon" href="./images/favicon.ico">
77
<meta name="viewport" content="width=device-width, initial-scale=1">
88

9-
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"></script>
9+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
1010
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
1111
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/codemirror.min.js"></script>
1212
<link href="https://cdn.jsdelivr.net/npm/[email protected]/lib/codemirror.min.css" rel="stylesheet">
13-
<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js "></script>
1413
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js "></script>
1514
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
1615

@@ -379,6 +378,162 @@ <h5 class="modal-title pixel-font" id="specialkeysModalLabel">Controls and Optio
379378
</div>
380379
</div>
381380

381+
<!-- Modal: Kubernetes Namespaces Help -->
382+
<div class="modal fade" id="k8sNamespacesHelpModal" tabindex="-1" aria-labelledby="k8sNamespacesHelpModalLabel" aria-hidden="true">
383+
<div class="modal-dialog modal-lg modal-dialog-scrollable">
384+
<div class="modal-content">
385+
<div class="modal-header">
386+
<h5 class="modal-title" id="k8sNamespacesHelpModalLabel">Kubernetes Namespaces</h5>
387+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
388+
</div>
389+
<div class="modal-body">
390+
<p style="font-size: small; font-weight: normal;">Provide a <strong>comma-separated</strong> list of namespaces that KubeInvaders should target. Pods and resources across all listed namespaces will be included in the chaos session.</p>
391+
<p style="font-size: small; font-weight: normal;"><strong>Example:</strong></p>
392+
<div style="position: relative;">
393+
<button class="btn btn-sm btn-outline-secondary" style="position: absolute; top: 6px; right: 6px; font-size: 11px;" onclick="copyToClipboard('k8sNamespacesExample')">Copy</button>
394+
<pre id="k8sNamespacesExample" style="background: #1e1e1e; color: #d4d4d4; padding: 14px; border-radius: 6px; font-size: 11px;">default,kube-system,my-app</pre>
395+
</div>
396+
<p style="font-size: small; font-weight: normal; margin-top: 12px;">To list available namespaces in your cluster, run:</p>
397+
<div style="position: relative;">
398+
<button class="btn btn-sm btn-outline-secondary" style="position: absolute; top: 6px; right: 6px; font-size: 11px;" onclick="copyToClipboard('k8sGetNamespacesCmd')">Copy</button>
399+
<pre id="k8sGetNamespacesCmd" style="background: #1e1e1e; color: #d4d4d4; padding: 14px; border-radius: 6px; font-size: 11px;">kubectl get namespaces</pre>
400+
</div>
401+
</div>
402+
<div class="modal-footer">
403+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
404+
</div>
405+
</div>
406+
</div>
407+
</div>
408+
409+
<!-- Modal: Kubernetes Token Help -->
410+
<div class="modal fade" id="k8sTokenHelpModal" tabindex="-1" aria-labelledby="k8sTokenHelpModalLabel" aria-hidden="true">
411+
<div class="modal-dialog modal-lg modal-dialog-scrollable">
412+
<div class="modal-content">
413+
<div class="modal-header">
414+
<h5 class="modal-title" id="k8sTokenHelpModalLabel">How to create a Kubernetes Service Account Token</h5>
415+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
416+
</div>
417+
<div class="modal-body">
418+
<p style="font-size: small; font-weight: normal;">Run the following command to create the required service account, RBAC roles, and a long-lived token secret:</p>
419+
<div style="position: relative;">
420+
<button class="btn btn-sm btn-outline-secondary" style="position: absolute; top: 6px; right: 6px; font-size: 11px;" onclick="copyK8sTokenScript()">Copy</button>
421+
<pre id="k8sTokenScript" style="background: #1e1e1e; color: #d4d4d4; padding: 14px; border-radius: 6px; font-size: 11px; overflow-x: auto; white-space: pre;">cat &lt;&lt; 'EOF' | kubectl apply -f -
422+
apiVersion: v1
423+
kind: Namespace
424+
metadata:
425+
name: kubeinvaders
426+
---
427+
apiVersion: rbac.authorization.k8s.io/v1
428+
kind: ClusterRole
429+
metadata:
430+
name: kinv-cr
431+
rules:
432+
- apiGroups:
433+
- ""
434+
resources:
435+
- pods
436+
- pods/log
437+
verbs:
438+
- delete
439+
- apiGroups:
440+
- batch
441+
- extensions
442+
resources:
443+
- jobs
444+
verbs:
445+
- get
446+
- list
447+
- watch
448+
- create
449+
- update
450+
- patch
451+
- delete
452+
- apiGroups:
453+
- "*"
454+
resources:
455+
- "*"
456+
verbs:
457+
- get
458+
- watch
459+
- list
460+
---
461+
apiVersion: v1
462+
kind: ServiceAccount
463+
metadata:
464+
name: kinv-sa
465+
namespace: kubeinvaders
466+
---
467+
apiVersion: rbac.authorization.k8s.io/v1
468+
kind: ClusterRoleBinding
469+
metadata:
470+
name: kinv-crb
471+
roleRef:
472+
apiGroup: rbac.authorization.k8s.io
473+
kind: ClusterRole
474+
name: kinv-cr
475+
subjects:
476+
- kind: ServiceAccount
477+
name: kinv-sa
478+
namespace: kubeinvaders
479+
---
480+
apiVersion: v1
481+
kind: Secret
482+
type: kubernetes.io/service-account-token
483+
metadata:
484+
name: kinv-sa-token
485+
namespace: kubeinvaders
486+
annotations:
487+
kubernetes.io/service-account.name: kinv-sa
488+
---
489+
apiVersion: rbac.authorization.k8s.io/v1
490+
kind: ClusterRole
491+
metadata:
492+
namespace: default
493+
name: kubevirt-vm-restart-role
494+
rules:
495+
- apiGroups: ["subresources.kubevirt.io"]
496+
resources: ["virtualmachines/restart"]
497+
verbs: ["update"]
498+
---
499+
apiVersion: rbac.authorization.k8s.io/v1
500+
kind: ClusterRoleBinding
501+
metadata:
502+
name: kubevirt-vm-restart-binding
503+
namespace: default
504+
subjects:
505+
- kind: ServiceAccount
506+
name: kubeinvaders
507+
namespace: kubeinvaders
508+
roleRef:
509+
kind: ClusterRole
510+
name: kubevirt-vm-restart-role
511+
apiGroup: rbac.authorization.k8s.io
512+
EOF</pre>
513+
</div>
514+
<p style="font-size: small; font-weight: normal; margin-top: 12px;">After applying, retrieve the token with:</p>
515+
<div style="position: relative;">
516+
<button class="btn btn-sm btn-outline-secondary" style="position: absolute; top: 6px; right: 6px; font-size: 11px;" onclick="copyToClipboard('k8sGetTokenCmd')">Copy</button>
517+
<pre id="k8sGetTokenCmd" style="background: #1e1e1e; color: #d4d4d4; padding: 14px; border-radius: 6px; font-size: 11px;">kubectl get secret kinv-sa-token -n kubeinvaders -o jsonpath='{.data.token}' | base64 --decode</pre>
518+
</div>
519+
</div>
520+
<div class="modal-footer">
521+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
522+
</div>
523+
</div>
524+
</div>
525+
</div>
526+
<script>
527+
function copyToClipboard(elementId) {
528+
const text = document.getElementById(elementId).innerText;
529+
navigator.clipboard.writeText(text);
530+
}
531+
function copyK8sTokenScript() {
532+
const raw = document.getElementById('k8sTokenScript').innerText;
533+
navigator.clipboard.writeText(raw);
534+
}
535+
</script>
536+
382537
<div class="container" id="gameContainer">
383538
<!-- START FIRST ROW -->
384539
<div class="row custom-btn-group" style="margin-top: 2%;">
@@ -392,10 +547,10 @@ <h2>Kubernetes Connection</h2>
392547
<label for="k8s_api_endpoint">Kubernetes API Endpoint</label>
393548
<input type="text" id="k8s_api_endpoint" name="k8s_api_endpoint" placeholder="https://kubernetes.default.svc">
394549

395-
<label for="k8s_token">Kubernetes Token</label>
550+
<label for="k8s_token">Kubernetes Token &nbsp;<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#k8sTokenHelpModal" title="How to create a service account token" style="padding: 1px 6px; font-size: 11px; line-height: 1.4;">&#9432;</button></label>
396551
<input type="password" id="k8s_token" name="k8s_token" placeholder="Enter your token">
397552

398-
<label for="k8s_namespaces">Kubernetes Namespaces</label>
553+
<label for="k8s_namespaces">Kubernetes Namespaces &nbsp;<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#k8sNamespacesHelpModal" title="About Kubernetes Namespaces" style="padding: 1px 6px; font-size: 11px; line-height: 1.4;">&#9432;</button></label>
399554
<input
400555
type="text"
401556
id="k8s_namespaces"

html/js/chaos_report.js

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -247,21 +247,6 @@ function sendSavedChaosReport() {
247247
chaos_report_start_date = new Date();
248248
}
249249

250-
//startGameMode()
251-
chaos_report_switch = true;
252-
document.getElementById("httpStatsCanvasDiv").style.display = "block";
253-
document.getElementById("chartDiv").style.display = "block";
254-
255-
drawCanvasHTTPStatusCodeStats();
256-
257-
chaosReportprojectName = presetBodyDict["chaosReportProject"];
258-
$("#chaosReportAuthorDiv").html(presetBodyDict["chaosReportAuthor"]);
259-
$("#chaosReportProjectDiv").html(presetBodyDict["chaosReportProject"]);
260-
$("#chaosReportDateDiv").html(chaos_report_start_date.toLocaleString());
261-
$("#chaosReportSessionTimeDiv").html(diffBetweenTwoDates(chaos_report_start_date, new Date()) + " seconds");
262-
$("#chaosReportCheckSiteURLDiv").html(presetBodyDict["chaosReportCheckSiteURL"]);
263-
264-
265250
if (headerAreLikePythonRequestHeaders(presetBodyDict["chaosReportCheckSiteURLHeaders"]) == false) {
266251
alert("Invalid headers. Insert them like this: \"Content-Type\": \"application/json; charset=utf-8\";\"Authorization\"");
267252
return;
@@ -277,6 +262,20 @@ function sendSavedChaosReport() {
277262
return;
278263
}
279264

265+
// Enable charts only after all validations pass.
266+
chaos_report_switch = true;
267+
chaosReportprojectName = presetBodyDict["chaosReportProject"];
268+
document.getElementById("httpStatsCanvasDiv").style.display = "block";
269+
document.getElementById("chartDiv").style.display = "block";
270+
271+
$("#chaosReportAuthorDiv").html(presetBodyDict["chaosReportAuthor"]);
272+
$("#chaosReportProjectDiv").html(presetBodyDict["chaosReportProject"]);
273+
$("#chaosReportDateDiv").html(chaos_report_start_date.toLocaleString());
274+
$("#chaosReportSessionTimeDiv").html(diffBetweenTwoDates(chaos_report_start_date, new Date()) + " seconds");
275+
$("#chaosReportCheckSiteURLDiv").html(presetBodyDict["chaosReportCheckSiteURL"]);
276+
277+
drawCanvasHTTPStatusCodeStats();
278+
280279
var oReq = new XMLHttpRequest();
281280
oReq.open("POST", k8s_url + "/chaos/report/save?project=" + $("#chaosReportProject").val(), true);
282281

@@ -291,6 +290,7 @@ function sendSavedChaosReport() {
291290
oReq.send(JSON.stringify(presetBodyDict));
292291
closePrepareChaosReportModal();
293292
resizeCharts();
293+
setTimeout(resizeCharts, 100);
294294
document.getElementById("myCanvas").scrollIntoView(true);
295295
document.getElementById("flagChaosReport").checked = true;
296296
$('#alert_placeholder').replaceWith(alert_div + 'RETURN TO TOP, PRESS START TO BEGIN AUTOMATIC SESSION </div>');
@@ -320,6 +320,10 @@ function saveChaosReport() {
320320
}
321321

322322
function updateElapsedTimeArray(projectName) {
323+
if (!projectName || projectName.trim() === "") {
324+
return;
325+
}
326+
323327
$("#chaosReportSessionTimeDiv").html(diffBetweenTwoDates(chaos_report_start_date, new Date()) + " seconds");
324328
// console.log("[SAVE-CHAOS-REPORT-CONF] Diff Between Dates: " + toString(diffBetweenTwoDates(chaos_report_start_date, new Date())));
325329
// console.log("[SAVE-CHAOS-REPORT-CONF] Updating elapsed time array for project: " + projectName);
@@ -330,10 +334,12 @@ function updateElapsedTimeArray(projectName) {
330334

331335
oReq.onreadystatechange = function () {
332336
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
333-
// console.log("[SAVE-CHAOS-REPORT-CONF] Elapsed time array received from Redis: " + parseFloat(this.responseText));
334-
chaos_report_http_elapsed_time_array.push(parseFloat(this.responseText));
335-
while (chaos_report_http_elapsed_time_array.length > 40) {
336-
chaos_report_http_elapsed_time_array.shift();
337+
var elapsed = parseFloat(this.responseText);
338+
if (!isNaN(elapsed) && isFinite(elapsed)) {
339+
chaos_report_http_elapsed_time_array.push(elapsed);
340+
while (chaos_report_http_elapsed_time_array.length > 40) {
341+
chaos_report_http_elapsed_time_array.shift();
342+
}
337343
}
338344
}
339345
};;
@@ -345,6 +351,10 @@ function updateElapsedTimeArray(projectName) {
345351
}
346352

347353
function updateStatusCodePieChart(projectName) {
354+
if (!projectName || projectName.trim() === "") {
355+
return;
356+
}
357+
348358
// console.log("[SAVE-CHAOS-REPORT-CONF] Updating Status Code Pie Chart for project: " + projectName);
349359

350360
var oReq = new XMLHttpRequest();
@@ -354,9 +364,18 @@ function updateStatusCodePieChart(projectName) {
354364
oReq.onreadystatechange = function () {
355365
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
356366
var status_code = this.responseText.trim();
357-
// console.log("[SAVE-CHAOS-REPORT-CONF] Status Code Pie Chart received from Redis: |" + status_code + "|");
367+
if (status_code === "" || status_code === "Key not found") {
368+
return;
369+
}
370+
if (!(status_code in chart_status_code_dict)) {
371+
if (/^\d{3}$/.test(status_code)) {
372+
status_code = "Other";
373+
} else {
374+
status_code = "Connection Error";
375+
}
376+
}
358377

359-
chart_status_code_dict[status_code] = chart_status_code_dict[status_code] + 1
378+
chart_status_code_dict[status_code] = (chart_status_code_dict[status_code] || 0) + 1
360379

361380
myHTTPStatusCodeChart.setOption({
362381
series: [

html/js/kubeinvaders.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,13 @@ window.setInterval(function draw() {
841841
ctx.fillStyle = 'white';
842842
ctx.font = "18px 'Ubuntu Mono'";
843843

844-
ctx.fillText('Cluster: ' + endpoint, 10, startYforHelp);
844+
if (localStorage.getItem('k8s_api_endpoint') != "") {
845+
ctx.fillText('API Endpoint: ' + localStorage.getItem('k8s_api_endpoint'), 10, startYforHelp);
846+
}
847+
else if (endpoint != "") {
848+
ctx.fillText('Cluster: ' + endpoint, 10, startYforHelp);
849+
850+
}
845851
ctx.fillText('Current Namespace: ' + namespace, 10, startYforHelp + 20);
846852
ctx.fillText('Alien Shuffle: ' + shuffle, 10, startYforHelp + 40);
847853
ctx.fillText('Auto Namespaces Switch: ' + namespacesJumpStatus, 10, startYforHelp + 60);
@@ -1018,7 +1024,10 @@ document.addEventListener("keyup", keyUpHandler, false);
10181024
setSystemSettings();
10191025

10201026
waitForReachableK8sUrl(function () {
1021-
getEndpoint();
1027+
if (endpoint != null && endpoint != "") {
1028+
console.log("[K-INV] Connected to Kubernetes API at " + endpoint);
1029+
getEndpoint();
1030+
}
10221031
getNamespaces();
10231032
getSavedPresets();
10241033
});

0 commit comments

Comments
 (0)