const clusterSelect = $("#cluster"); const chartsCards = $("#charts"); const revRow = $("#sectionDetails .row"); function reportError(err) { alert(err) // TODO: nice modal/baloon/etc } function revisionClicked(namespace, name, self) { let active = "active border-primary border-2 bg-opacity-25 bg-primary"; let inactive = "border-secondary bg-white"; revRow.find(".active").removeClass(active).addClass(inactive) self.removeClass(inactive).addClass(active) const elm = self.data("elm") setHashParam("revision", elm.revision) $("#sectionDetails h1 span.rev").text(elm.revision) $("#chartName").text(elm.chart) $("#revDescr").text(elm.description).removeClass("text-danger") if (elm.status === "failed") { $("#revDescr").addClass("text-danger") } const tab = getHashParam("tab") if (!tab) { $("#nav-tab [data-tab=manifests]").click() } else { $("#nav-tab [data-tab=" + tab + "]").click() } } $("#nav-tab [data-tab]").click(function () { const self = $(this) setHashParam("tab", self.data("tab")) if (self.data("tab") === "values") { $("#userDefinedVals").parent().show() } else { $("#userDefinedVals").parent().hide() } const flag = getHashParam("udv") === "true"; $("#userDefinedVals").prop("checked", flag) const mode = getHashParam("mode") if (!mode) { $("#modePanel [data-mode=diff-prev]").trigger('click') } else { $("#modePanel [data-mode=" + mode + "]").trigger('click') } }) $("#modePanel [data-mode]").click(function () { const self = $(this) const mode = self.data("mode") setHashParam("mode", mode) loadContentWrapper() }) $("#userDefinedVals").change(function () { const self = $(this) const flag = $("#userDefinedVals").prop("checked"); setHashParam("udv", flag) loadContentWrapper() }) function loadContentWrapper() { let revDiff = 0 const revision = parseInt(getHashParam("revision")); if (getHashParam("mode") === "diff-prev") { revDiff = revision - 1 } else if (getHashParam("mode") === "diff-rev") { revDiff = $("#specRev").val() } const flag = $("#userDefinedVals").prop("checked"); loadContent(getHashParam("tab"), getHashParam("namespace"), getHashParam("chart"), revision, revDiff, flag) } function loadContent(mode, namespace, name, revision, revDiff, flag) { let qstr = "chart=" + name + "&namespace=" + namespace + "&revision=" + revision if (revDiff) { qstr += "&revisionDiff=" + revDiff } if (flag) { qstr += "&flag=" + flag } let url = "/api/helm/charts/" + mode url += "?" + qstr const diffDisplay = $("#manifestText"); diffDisplay.empty().append("") $.get(url).fail(function () { reportError("Failed to get diff of " + mode) }).done(function (data) { diffDisplay.empty(); if (data === "") { diffDisplay.text("No differences to display") } else { if (revDiff) { const targetElement = document.getElementById('manifestText'); const configuration = { inputFormat: 'diff', outputFormat: 'side-by-side', drawFileList: false, showFiles: false, highlight: true, //matching: 'lines', }; const diff2htmlUi = new Diff2HtmlUI(targetElement, data, configuration); diff2htmlUi.draw() } else { data = hljs.highlight(data, {language: 'yaml'}).value const code = $("#manifestText").empty().append("
").find("pre");
                code.html(data)
            }
        }
    })
}

$('#specRev').keyup(function (event) {
    let keycode = (event.keyCode ? event.keyCode : event.which);
    if (keycode == '13') {
        $("#diffModeRev").click()
    }
    event.preventDefault()
});

function loadChartHistory(namespace, name) {
    $("#sectionDetails").show()
    $("#sectionDetails h1 span.name").text(name)
    revRow.empty().append("
") $.getJSON("/api/helm/charts/history?chart=" + name + "&namespace=" + namespace).fail(function () { reportError("Failed to get list of clusters") }).done(function (data) { revRow.empty() for (let x = 0; x < data.length; x++) { const elm = data[x] $("#specRev").val(elm.revision) const rev = $(`
-
Chart:
App ver:

Age:

`) 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-age").text(getAge(elm, data[x + 1])) rev.find(".rev-status").text(elm.status) rev.find(".fa").attr("title", elm.action) if (elm.status === "failed") { rev.find(".rev-status").parent().addClass("text-danger") } switch (elm.action) { case "app_upgrade": rev.find(".app-ver").append(" ") break case "app_downgrade": rev.find(".app-ver").append(" ") break case "chart_upgrade": rev.find(".chart-ver").append(" ") break case "chart_downgrade": rev.find(".chart-ver").append(" ") break case "reconfigure": // ? break } rev.data("elm", elm) rev.addClass("rev-" + elm.revision) rev.click(function () { revisionClicked(namespace, name, $(this)) }) revRow.append(rev) } const rev = getHashParam("revision") if (rev) { revRow.find(".rev-" + rev).click() } else { revRow.find("div.col-md-2:last-child").click() } }) } function getHashParam(name) { const params = new URLSearchParams(window.location.hash.substring(1)) return params.get(name) } function setHashParam(name, val) { const params = new URLSearchParams(window.location.hash.substring(1)) params.set(name, val) window.location.hash = new URLSearchParams(params).toString() } function loadChartsList() { $("#sectionList").show() chartsCards.empty().append("
Loading...
") $.getJSON("/api/helm/charts").fail(function () { reportError("Failed to get list of clusters") }).done(function (data) { chartsCards.empty() data.forEach(function (elm) { const header = $("
") header.append($('
#' + elm.revision + '

' + elm.status + "
")) // 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($('
').text(elm.name)) header.append($('

').append("Chart: " + elm.chart)) const body = $("
") body.append($('

').append("Namespace: " + elm.namespace)) body.append($('

').append("Version: " + elm.app_version)) body.append($('

').append("Updated: " + elm.updated)) let card = $("
").append(header).append(body); card.data("chart", elm) card.click(function () { const self = $(this) $("#sectionList").hide() let chart = self.data("chart"); setHashParam("namespace", chart.namespace) setHashParam("chart", chart.name) loadChartHistory(chart.namespace, chart.name) }) chartsCards.append($("
").append(card)) }) }) } $(function () { // cluster list clusterSelect.change(function () { Cookies.set("context", clusterSelect.val()) window.location.href = "/" }) $.getJSON("/api/kube/contexts").fail(function () { reportError("Failed to get list of clusters") }).done(function (data) { const context = Cookies.get("context") data.forEach(function (elm) { // aws CLI uses complicated context names, the suffix does not work well // maybe we should have an `if` statement here let label = elm.Name //+ " (" + elm.Cluster + "/" + elm.AuthInfo + "/" + elm.Namespace + ")" let opt = $("").val(elm.Name).text(label) if (elm.IsCurrent && !context) { opt.attr("selected", "selected") } else if (context && elm.Name === context) { opt.attr("selected", "selected") $.ajaxSetup({ headers: { 'x-kubecontext': context } }); } clusterSelect.append(opt) }) const namespace = getHashParam("namespace") const chart = getHashParam("chart") if (!chart) { loadChartsList() } else { loadChartHistory(namespace, chart) } }) }) function getAge(obj1, obj2) { const date = luxon.DateTime.fromISO(obj1.updated); let dateNext = luxon.DateTime.now() if (obj2) { dateNext = luxon.DateTime.fromISO(obj2.updated); } const diff = dateNext.diff(date); const map = { "years": "yr", "months": "mo", "days": "d", "hours": "h", "minutes": "m", "seconds": "s", "milliseconds": "ms" } for (let unit of ["years", "months", "days", "hours", "minutes", "seconds", "milliseconds"]) { const val = diff.as(unit); if (val >= 1) { return Math.round(val) + map[unit] } } return "n/a" }