Chart details draft (#4)

* Add logo

* Refactor out structs

* Data layer context-awareness

* Mod

* Data layer improvements

* Progress

* Progress

* Progress

* Figured the time format shorter

* Statuses colors

* Sticky URL

* Calculate some diffs inside

* Separate checks

* Scrap gofmt

* Skip custom test in GH

* Shows some colorful diff
This commit is contained in:
Andrey Pokhilko
2022-08-31 12:12:08 +01:00
committed by GitHub
parent d9a88feb7b
commit 1580c2e9a0
16 changed files with 699 additions and 203 deletions

View File

@@ -1,58 +1,125 @@
<!doctype html>
<html lang="en">
<head>
<link rel="icon" href="https://komodor.com/wp-content/uploads/2021/05/favicon-50x50.png"/>
<link rel="icon" href="static/logo.png"/>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Helm Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css" />
<link href="static/styles.css" rel="stylesheet">
</head>
<body>
<div class="container">
<nav class="navbar navbar-expand-lg bg-light rounded" aria-label="Eleventh navbar example">
<i class="fa-solid fa-arrow-trend-down"></i>
<nav class="navbar navbar-expand-lg bg-light rounded" style="margin-bottom: 0.75rem">
<div class="container-fluid">
<div style="line-height: 90%">
<div style="line-height: 90%; font-size: 1.5rem" class="navbar-brand">
<img src="static/logo.png" style="height: 3rem; float: left" alt="Logo">
<a class="navbar-brand" href="#"><b>Helm Dashboard</b></a><br/>
<span style="font-size: smaller">by <a href="https://komodor.io">komodor.io</a></span>
<span style="font-size: 0.8rem;">by <a href="https://komodor.io">komodor.io</a></span>
</div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarsExample09"
aria-controls="navbarsExample09" aria-expanded="false" aria-label="Toggle navigation">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav"
aria-controls="mainNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExample09">
<div class="collapse navbar-collapse" id="mainNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">Charts List</a>
<a class="nav-link active" aria-current="page" href="/">Installed Charts</a>
</li>
<li class="nav-item">
<a class="nav-link disabled">Provisional Charts</a>
</li>
<li class="nav-item">
<a class="nav-link disabled">Repositories</a>
</li>
</ul>
<form class="d-flex flex-nowrap text-nowrap">
<label for="cluster" class="">K8s Context:</label>
<label for="cluster" style="margin-top: 0.5rem">K8s Context:</label>
<select id="cluster" class="form-control"></select>
</form>
</div>
</div>
</nav>
<div class="bg-light p-5 pt-0 rounded" id="sectionDetails" style="display: none">
<span class="text-muted" style="transform: rotate(270deg); z-index: 100; display: inline-block; position: relative; left:-4rem; top: 4rem; color: #BBB!important; text-transform: uppercase">Revisions</span>
<div class="row mb-3">
<div class="bg-light p-5 rounded">
</div>
<h1><a href="/" class="text-reset" style="text-decoration: none">&lArr;</a> Chart Details: <span class="name">chosen-one</span>,
revision <span class="rev"></span></h1>
<nav>
<div class="nav nav-tabs" id="nav-tab" role="tablist">
<button class="nav-link active" id="nav-manifest-diff-tab" data-bs-toggle="tab" data-bs-target="#nav-manifest-diff"
type="button" role="tab" aria-controls="nav-manifest-diff" aria-selected="true">Manifests
</button>
<button class="nav-link" id="nav-disabled-tab" data-bs-toggle="tab" data-bs-target="#nav-disabled"
type="button" role="tab" aria-controls="nav-disabled" aria-selected="false" disabled>Resources
</button>
<button class="nav-link" id="nav-disabled-tab" data-bs-toggle="tab" data-bs-target="#nav-disabled"
type="button" role="tab" aria-controls="nav-disabled" aria-selected="false" disabled>Parameterized Values
</button>
<button class="nav-link" id="nav-disabled-tab" data-bs-toggle="tab" data-bs-target="#nav-disabled"
type="button" role="tab" aria-controls="nav-disabled" aria-selected="false" disabled>Notes
</button>
</div>
</nav>
<div class="tab-content" id="nav-tabContent">
<div class="tab-pane fade show active" id="nav-manifest-diff" role="tabpanel" aria-labelledby="nav-manifest-diff-tab"
tabindex="0">
<nav class="navbar bg-light">
<form class="container-fluid">
<label class="form-check-label" for="diffModeNone">
<input class="form-check-input" type="radio" name="diffMode" id="diffModeNone" disabled>
View Manifests
</label>
<label class="form-check-label" for="diffModePrev">
<input class="form-check-input" type="radio" name="diffMode" id="diffModePrev">
Diff with previous
</label>
<label class="form-check-label" for="diffModeLatest">
<input class="form-check-input" type="radio" name="diffMode" id="diffModeLatest" disabled>
Diff with latest <span class="text-muted">(#5)</span>
</label>
<label class="form-check-label" for="diffModeRev">
<input class="form-check-input" type="radio" name="diffMode" id="diffModeRev" disabled>
Diff with specific revision: <input class="form-input" size="3" disabled>
</label>
</form>
</nav>
<div id="manifestText" class="mt-2"></div>
</div>
<div class="tab-pane fade" id="nav-disabled" role="tabpanel" aria-labelledby="nav-disabled-tab"
tabindex="0">...
</div>
</div>
</div>
<div class="bg-light p-5 rounded" id="sectionList" style="display: none">
<h1>Charts List</h1>
<div id="charts" class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa"
crossorigin="anonymous"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
<script src="static/scripts.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -5,6 +5,126 @@ function reportError(err) {
alert(err) // TODO: nice modal/baloon/etc
}
function revisionClicked(namespace, name, self) {
const elm = self.data("elm")
const parts = window.location.hash.split("&")
parts[2] = elm.revision
window.location.hash = parts.join("&")
$("#sectionDetails h1 span.rev").text(elm.revision)
let qstr = "chart=" + name + "&namespace=" + namespace + "&revision1=" + (elm.revision - 1) + "&revision2=" + elm.revision
let url = "/api/helm/charts/manifest/diff?" + qstr;
$.getJSON(url).fail(function () {
reportError("Failed to get diff of manifests")
}).done(function (data) {
if (data === "") {
$("#manifestText").text("No differences to display")
} else {
const targetElement = document.getElementById('manifestText');
const configuration = {
inputFormat: 'diff',
outputFormat: 'side-by-side',
drawFileList: false,
showFiles: false,
//matching: 'lines',
};
const diff2htmlUi = new Diff2HtmlUI(targetElement, data, configuration);
diff2htmlUi.draw()
}
})
}
function fillChartDetails(namespace, name) {
$("#sectionDetails").show()
$("#sectionDetails h1 span.name").text(name)
$.getJSON("/api/helm/charts/history?chart=" + name + "&namespace=" + namespace).fail(function () {
reportError("Failed to get list of clusters")
}).done(function (data) {
let revRow = $("#sectionDetails .row");
for (let x = 0; x < data.length; x++) {
const elm = data[x]
const rev = $(`<div class="col-md-2 rounded border border-secondary bg-gradient bg-white">
<span><b class="rev-number"></b> - <span class="rev-status"></span></span><br/>
<span class="text-muted">Chart:</span> <span class="chart-ver"></span><br/>
<span class="text-muted">App:</span> <span class="app-ver"></span><br/>
<span class="text-muted small rev-date"></span><br/>
</div>`)
rev.find(".rev-number").text("#" + elm.revision)
rev.find(".app-ver").text(elm.app_version)
rev.find(".chart-ver").text(elm.chart_ver)
rev.find(".rev-date").text(elm.updated.replace("T", " "))
rev.find(".rev-status").text(elm.status).attr("title", elm.action)
if (elm.status === "failed") {
rev.find(".rev-status").parent().addClass("text-danger")
}
if (elm.status === "deployed") {
//rev.removeClass("bg-white").addClass("text-light bg-primary")
}
rev.data("elm", elm)
rev.addClass("rev-" + elm.revision)
rev.click(function () {
revisionClicked(namespace, name, $(this))
})
revRow.append(rev)
}
const parts = window.location.hash.substring(1).split("&")
if (parts.length >= 3) {
revRow.find(".rev-" + parts[2]).click()
} else {
revRow.find("div.col-md-2:last-child").click()
}
})
}
function loadChartsList() {
$("#sectionList").show()
$.getJSON("/api/helm/charts").fail(function () {
reportError("Failed to get list of clusters")
}).done(function (data) {
chartsCards.empty()
data.forEach(function (elm) {
const header = $("<div class='card-header'></div>")
header.append($('<div class="float-end"><h5 class="float-end text-muted text-end">#' + elm.revision + '</h5><br/><div class="badge">' + elm.status + "</div>"))
// TODO: for pending- and uninstalling, add the spinner
if (elm.status === "failed") {
header.find(".badge").addClass("bg-danger text-light")
} else if (elm.status === "deployed" || elm.status === "superseded") {
header.find(".badge").addClass("bg-info")
} else {
header.find(".badge").addClass("bg-light text-dark")
}
header.append($('<h5 class="card-title"></h5>').text(elm.name))
header.append($('<p class="card-text small text-muted"></p>').append("Chart: " + elm.chart))
const body = $("<div class='card-body'></div>")
body.append($('<p class="card-text"></p>').append("Namespace: " + elm.namespace))
body.append($('<p class="card-text"></p>').append("Version: " + elm.app_version))
body.append($('<p class="card-text"></p>').append("Updated: " + elm.updated))
let card = $("<div class='card'></div>").append(header).append(body);
card.data("chart", elm)
card.click(function () {
const self = $(this)
$("#sectionList").hide()
let chart = self.data("chart");
window.location.hash = chart.namespace + "&" + chart.name
fillChartDetails(chart.namespace, chart.name)
})
chartsCards.append($("<div class='col'></div>").append(card))
})
})
}
$(function () {
// cluster list
$.getJSON("/api/kube/contexts").fail(function () {
@@ -25,34 +145,10 @@ $(function () {
// TODO: remember it, respect it in the function above and in all other places
})
// charts list
$.getJSON("/api/helm/charts").fail(function () {
reportError("Failed to get list of clusters")
}).done(function (data) {
chartsCards.empty()
data.forEach(function (elm) {
const header = $("<div class='card-header'></div>")
header.append($('<div class="float-end"><h5 class="float-end text-muted text-end">#' + elm.revision + '</h5><br/><div class="badge bg-info">' + elm.status + "</div>"))
header.append($('<h5 class="card-title"></h5>').text(elm.name))
header.append($('<p class="card-text small text-muted"></p>').append("Version: " + elm.app_version))
const body = $("<div class='card-body'></div>")
body.append($('<p class="card-text"></p>').append("Namespace: " + elm.namespace))
body.append($('<p class="card-text"></p>').append("Chart: " + elm.chart))
body.append($('<p class="card-text"></p>').append("Updated: " + elm.updated))
/*
"namespace": "default",
"revision": "4",
"updated": "2022-08-16 17:11:26.73393511 +0300 IDT",
"status": "deployed",
"chart": "k8s-watcher-0.17.1",
"app_version": "0.1.108"
*/
let card = $("<div class='card'></div>").append(header).append(body);
chartsCards.append($("<div class='col'></div>").append(card))
})
})
const parts = window.location.hash.substring(1).split("&")
if (parts[0] === "") {
loadChartsList()
} else {
fillChartDetails(parts[0], parts[1])
}
})

View File

@@ -1,3 +1,11 @@
#charts .card {
#charts .card, #sectionDetails .row > div {
cursor: pointer;
}
.bg-primary .text-muted {
color: white!important;
}
.d2h-file-header {
display: none;
}