mirror of
https://github.com/komodorio/helm-dashboard.git
synced 2026-03-24 11:48:04 +00:00
Scanners Integration (#18)
* Research scanning * Move files around * Reports the list * Scanner happens * Commit * Work on alternative * refactorings * Progress * Save the state * Commit * Display trivy Results * Checkov also reports * Better display * Correct trivy numbers * Scan pre-install manifest * Readme items * Static checks
This commit is contained in:
@@ -4,7 +4,7 @@ $("#btnUpgradeCheck").click(function () {
|
||||
self.find(".spinner-border").show()
|
||||
const repoName = self.data("repo")
|
||||
$("#btnUpgrade span").text("Checking...")
|
||||
$("#btnUpgrade .icon").removeClass("bi-arrow-up bi-pencil").addClass("bi-hourglass")
|
||||
$("#btnUpgrade .icon").removeClass("bi-arrow-up bi-pencil").addClass("bi-hourglass-split")
|
||||
$.post("/api/helm/repo/update?name=" + repoName).fail(function (xhr) {
|
||||
reportError("Failed to update chart repo", xhr)
|
||||
}).done(function () {
|
||||
@@ -63,8 +63,9 @@ function checkUpgradeable(name) {
|
||||
|
||||
function popUpUpgrade(self, verCur, elm) {
|
||||
const name = getHashParam("chart");
|
||||
let url = "/api/helm/charts/install?namespace=" + getHashParam("namespace") + "&name=" + name + "&chart=" + elm.name;
|
||||
$('#upgradeModal select').data("url", url).data("chart", elm.name)
|
||||
const qstr = "?namespace=" + getHashParam("namespace") + "&name=" + name + "&chart=" + elm.name;
|
||||
let url = "/api/helm/charts/install" + qstr
|
||||
$('#upgradeModal select').data("qstr", qstr).data("url", url).data("chart", elm.name)
|
||||
|
||||
$("#upgradeModalLabel .name").text(name)
|
||||
$("#upgradeModal .ver-old").text(verCur)
|
||||
@@ -84,7 +85,6 @@ function popUpUpgrade(self, verCur, elm) {
|
||||
}).fail(function (xhr) {
|
||||
reportError("Failed to upgrade the chart", xhr)
|
||||
}).done(function (data) {
|
||||
console.log(data)
|
||||
if (data.version) {
|
||||
setHashParam("revision", data.version)
|
||||
window.location.reload()
|
||||
@@ -130,6 +130,40 @@ $('#upgradeModal select').change(function () {
|
||||
})
|
||||
})
|
||||
|
||||
$('#upgradeModal .btn-scan').click(function () {
|
||||
const self = $(this)
|
||||
|
||||
self.prop("disabled", true).prepend('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>')
|
||||
const qstr = $('#upgradeModal select').data("qstr")
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/api/scanners/manifests" + qstr + "&version=" + $('#upgradeModal select').val(),
|
||||
data: $("#upgradeModal form").serialize(),
|
||||
}).fail(function (xhr) {
|
||||
reportError("Failed to scan the manifest", xhr)
|
||||
}).done(function (data) {
|
||||
self.prop("disabled", false).find(".spinner-border").hide()
|
||||
|
||||
const container = $("<div></div>")
|
||||
for (let name in data) {
|
||||
const res = data[name]
|
||||
|
||||
if (!res) {
|
||||
continue
|
||||
}
|
||||
|
||||
const pre = $("<pre></pre>").text(res.OrigReport)
|
||||
|
||||
container.append("<h2>" + name + " Scan Results</h2>")
|
||||
container.append(pre)
|
||||
}
|
||||
|
||||
const tab = window.open('about:blank', '_blank');
|
||||
tab.document.write(container.prop('outerHTML')); // where 'html' is a variable containing your HTML
|
||||
tab.document.close(); // to finish loading the page
|
||||
})
|
||||
})
|
||||
|
||||
function requestChangeDiff() {
|
||||
const self = $('#upgradeModal select');
|
||||
const diffBody = $("#upgradeModalBody");
|
||||
@@ -140,11 +174,11 @@ function requestChangeDiff() {
|
||||
if ($("#upgradeModal textarea").data("dirty")) {
|
||||
$("#upgradeModal .invalid-feedback").hide()
|
||||
values = $("#upgradeModal form").serialize()
|
||||
|
||||
|
||||
try {
|
||||
jsyaml.load($("#upgradeModal textarea").val())
|
||||
} catch (e) {
|
||||
$("#upgradeModal .invalid-feedback").text("YAML parse error: "+e.message).show()
|
||||
$("#upgradeModal .invalid-feedback").text("YAML parse error: " + e.message).show()
|
||||
$("#upgradeModalBody").html("Invalid values YAML")
|
||||
return
|
||||
}
|
||||
@@ -155,7 +189,7 @@ function requestChangeDiff() {
|
||||
url: self.data("url") + "&version=" + self.val(),
|
||||
data: values,
|
||||
}).fail(function (xhr) {
|
||||
$("#upgradeModalBody").html("<p class='text-danger'>Failed to get upgrade info: "+ xhr.responseText+"</p>")
|
||||
$("#upgradeModalBody").html("<p class='text-danger'>Failed to get upgrade info: " + xhr.responseText + "</p>")
|
||||
}).done(function (data) {
|
||||
diffBody.empty();
|
||||
$("#upgradeModal .btn-confirm").prop("disabled", false)
|
||||
|
||||
@@ -97,7 +97,7 @@ $('#specRev').keyup(function (event) {
|
||||
}
|
||||
});
|
||||
|
||||
$("form").submit(function(e){
|
||||
$("form").submit(function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
@@ -140,6 +140,7 @@ $("#nav-tab [data-tab]").click(function () {
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
function showResources(namespace, chart, revision) {
|
||||
const resBody = $("#nav-resources .body");
|
||||
resBody.empty().append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>');
|
||||
@@ -156,9 +157,9 @@ function showResources(namespace, chart, revision) {
|
||||
<div class="row px-3 py-2 mb-3 bg-white rounded">
|
||||
<div class="col-2 res-kind text-break"></div>
|
||||
<div class="col-3 res-name text-break fw-bold"></div>
|
||||
<div class="col-1 res-status"><span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span></div>
|
||||
<div class="col-5 res-statusmsg"><span class="text-muted small">Getting status...</span></div>
|
||||
<div class="col-1 res-actions"></div>
|
||||
<div class="col-1 res-status overflow-hidden"><span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span></div>
|
||||
<div class="col-4 res-statusmsg"><span class="text-muted small">Getting status...</span></div>
|
||||
<div class="col-2 res-actions"></div>
|
||||
</div>
|
||||
`)
|
||||
|
||||
@@ -182,16 +183,23 @@ function showResources(namespace, chart, revision) {
|
||||
}
|
||||
|
||||
const statusBlock = resBlock.find(".res-status");
|
||||
statusBlock.empty().append(badge)
|
||||
statusBlock.empty().append(badge).attr("title", data.status.phase)
|
||||
resBlock.find(".res-statusmsg").html("<span class='text-muted small'>" + (data.status.message ? data.status.message : '') + "</span>")
|
||||
|
||||
if (badge.text() !== "NotFound") {
|
||||
resBlock.find(".res-actions")
|
||||
|
||||
const btn = $("<button class=\"btn btn-sm btn-white border-secondary\">Describe</button>");
|
||||
resBlock.find(".res-actions").append(btn)
|
||||
btn.click(function () {
|
||||
showDescribe(ns, res.kind, res.metadata.name, badge.clone())
|
||||
})
|
||||
|
||||
const btn2 = $("<button class='btn btn-sm btn-white border-secondary ms-2'>Scan</button>");
|
||||
resBlock.find(".res-actions").append(btn2)
|
||||
btn2.click(function () {
|
||||
scanResource(ns, res.kind, res.metadata.name, badge.clone())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -212,3 +220,42 @@ function showDescribe(ns, kind, name, badge) {
|
||||
$("#describeModalBody").empty().append("<pre class='bg-white rounded p-3'></pre>").find("pre").html(data)
|
||||
})
|
||||
}
|
||||
|
||||
function scanResource(ns, kind, name, badge) {
|
||||
$("#describeModal .offcanvas-header p").text(kind)
|
||||
$("#describeModalLabel").text(name).append(badge.addClass("ms-3 small fw-normal"))
|
||||
const body = $("#describeModalBody");
|
||||
body.empty().append('<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Scanning...')
|
||||
|
||||
const myModal = new bootstrap.Offcanvas(document.getElementById('describeModal'));
|
||||
myModal.show()
|
||||
$.get("/api/scanners/resource/" + kind.toLowerCase() + "?name=" + name + "&namespace=" + ns).fail(function (xhr) {
|
||||
reportError("Failed to scan resource", xhr)
|
||||
}).done(function (data) {
|
||||
body.empty()
|
||||
if ($.isEmptyObject(data)) {
|
||||
body.append("No information from scanners. Make sure you have installed some and scanned object is supported.")
|
||||
}
|
||||
|
||||
for (let name in data) {
|
||||
const res = data[name]
|
||||
|
||||
if (!res.OrigReport) continue
|
||||
const hdr = $("<h3>" + name + " Scan Results</h3>");
|
||||
|
||||
if (res.FailedCount) {
|
||||
hdr.append("<span class='badge bg-danger ms-3'>" + res.FailedCount + " failed</span>")
|
||||
}
|
||||
|
||||
if (res.PassedCount) {
|
||||
hdr.append("<span class='badge bg-info ms-3'>" + res.PassedCount + " passed</span>")
|
||||
}
|
||||
|
||||
body.append(hdr)
|
||||
|
||||
const hl = hljs.highlight(res.OrigReport, {language: 'yaml'}).value
|
||||
const pre = $("<pre class='bg-white rounded p-3' style='font-size: inherit; overflow: unset'></pre>").html(hl)
|
||||
body.append(pre)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -174,15 +174,15 @@
|
||||
Resources
|
||||
</button>
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nav-manifest" data-tab="manifests"
|
||||
type="button" role="tab" aria-controls="nav-manifest-diff" aria-selected="false"
|
||||
type="button" role="tab" aria-controls="nav-manifest" aria-selected="false"
|
||||
tabindex="-1">Manifests
|
||||
</button>
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nav-manifest" data-tab="values"
|
||||
type="button" role="tab" aria-controls="nav-disabled" aria-selected="false" tabindex="-1">
|
||||
type="button" role="tab" aria-controls="nav-manifest" aria-selected="false" tabindex="-1">
|
||||
Values
|
||||
</button>
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nav-manifest" data-tab="notes"
|
||||
type="button" role="tab" aria-controls="nav-disabled" aria-selected="false" tabindex="-1">
|
||||
type="button" role="tab" aria-controls="nav-manifest" aria-selected="false" tabindex="-1">
|
||||
Notes
|
||||
</button>
|
||||
</div>
|
||||
@@ -200,7 +200,7 @@
|
||||
<div class="body"></div>
|
||||
</div>
|
||||
<div class="tab-pane" id="nav-manifest" role="tabpanel">
|
||||
<nav class="navbar bg-light">
|
||||
<nav class="navbar bg-white rounded border border-secondary">
|
||||
<form class="container-fluid" id="modePanel">
|
||||
<label class="form-check-label" for="diffModeNone">
|
||||
<input class="form-check-input" type="radio" name="diffMode" id="diffModeNone"
|
||||
@@ -217,17 +217,11 @@
|
||||
data-mode="diff-rev">
|
||||
Diff with specific revision: <input class="form-input" size="3" id="specRev">
|
||||
</label>
|
||||
<label class="form-check-label" for="userDefinedVals">
|
||||
<input class="form-check-input" type="checkbox" id="userDefinedVals"> User-defined only
|
||||
</label>
|
||||
</form>
|
||||
</nav>
|
||||
|
||||
<div id="manifestText" class="mt-2 bg-white"></div>
|
||||
</div>
|
||||
<div class="tab-pane" id="nav-disabled" role="tabpanel" aria-labelledby="nav-disabled-tab"
|
||||
tabindex="0">...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -235,7 +229,7 @@
|
||||
|
||||
|
||||
<!-- Modals -->
|
||||
<div id="errorAlert" style="z-index: 2000"
|
||||
<div id="errorAlert" style="z-index: 2000; max-width: 95%; overflow: auto"
|
||||
class="display-none alert alert-sm alert-danger alert-dismissible position-absolute position-absolute top-0 start-50 translate-middle-x mt-3 border-danger"
|
||||
role="alert">
|
||||
<h4 class="alert-heading"><i class="bi-exclamation-triangle-fill"></i> <span></span></h4>
|
||||
@@ -245,7 +239,7 @@
|
||||
</div>
|
||||
|
||||
<div class="offcanvas offcanvas-end rounded-start" tabindex="-1" id="describeModal"
|
||||
aria-labelledby="describeModalLabel">
|
||||
aria-labelledby="describeModalLabel" style="overflow-x: auto">
|
||||
<div class="offcanvas-header border-bottom p-4">
|
||||
<div>
|
||||
<h5 id="describeModalLabel"></h5>
|
||||
@@ -302,7 +296,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 pe-3">
|
||||
<textarea name="values" class="form-control w-100 h-100" rows="5"></textarea>
|
||||
<textarea name="values" class="form-control w-100 h-100" rows="5" style="font-family: monospace"></textarea>
|
||||
</div>
|
||||
<div class="col-6 ps-3">
|
||||
<pre class="ref-vals fs-6 w-100 bg-secondary p-2 rounded" style="max-height: 20rem"></pre>
|
||||
@@ -314,10 +308,11 @@
|
||||
<span class="invalid-feedback small mb-3"> (wrong YAML)</span>
|
||||
</div>
|
||||
</div>
|
||||
<label class="form-label mt-5">Manifest changes:</label>
|
||||
<label class="form-label mt-4">Manifest changes:</label>
|
||||
<div id="upgradeModalBody" class="small"></div>
|
||||
</form>
|
||||
<div class="modal-footer">
|
||||
<div class="modal-footer d-flex">
|
||||
<button type="button" class="btn btn-scan bg-white border-secondary">Scan for Problems</button>
|
||||
<button type="button" class="btn btn-primary btn-confirm">Confirm Upgrade</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -336,6 +331,7 @@
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js"
|
||||
integrity="sha512-CSBhVREyzHAjAFfBlIBakjoRUKp5h7VSweP0InR/pAJyptH7peuhCsqAI/snV+TwZmXZqoUklpXp6R6wMnYf5Q=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
|
||||
<script src="static/list-view.js"></script>
|
||||
<script src="static/revisions-view.js"></script>
|
||||
<script src="static/details-view.js"></script>
|
||||
|
||||
@@ -19,6 +19,23 @@ $(function () {
|
||||
loadChartHistory(namespace, chart)
|
||||
}
|
||||
})
|
||||
|
||||
$.getJSON("/api/scanners").fail(function (xhr) {
|
||||
reportError("Failed to get list of scanners", xhr)
|
||||
}).done(function (data) {
|
||||
for (let n = 0; n < data.length; n++) {
|
||||
const item = $(`
|
||||
<label class="form-check-label me-4">
|
||||
<input class="form-check-input me-1" type="checkbox" checked name="scanner" value="` + data[n] + `"> ` + data[n] + `
|
||||
</label>`)
|
||||
|
||||
$("#nav-scanners form span").prepend(item)
|
||||
}
|
||||
|
||||
if (!data.length) {
|
||||
$("#upgradeModal .btn-scan").hide()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -69,14 +86,14 @@ function statusStyle(status, card, txt) {
|
||||
}
|
||||
|
||||
function getCleanClusterName(rawClusterName) {
|
||||
if (rawClusterName.indexOf('arn')==0) {
|
||||
if (rawClusterName.indexOf('arn') == 0) {
|
||||
// AWS cluster
|
||||
clusterSplit = rawClusterName.split(':')
|
||||
clusterName = clusterSplit.at(-1).split("/").at(-1)
|
||||
region = clusterSplit.at(-3)
|
||||
return region + "/" + clusterName + ' [AWS]'
|
||||
}
|
||||
if (rawClusterName.indexOf('gke')==0) {
|
||||
if (rawClusterName.indexOf('gke') == 0) {
|
||||
// GKE cluster
|
||||
return rawClusterName.split('_').at(-2) + '/' + rawClusterName.split('_').at(-1) + ' [GKE]'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user