Allow uninstalling the chart (#8)

This commit is contained in:
Andrey Pokhilko
2022-09-09 14:50:50 +01:00
committed by GitHub
parent 91fd3793c7
commit fa48cf5435
4 changed files with 132 additions and 41 deletions

View File

@@ -59,6 +59,11 @@ func configureRoutes(abortWeb ControlChan, data *DataLayer, api *gin.Engine) {
abortWeb <- struct{}{}
})
configureHelms(api, data)
configureKubectls(api, data)
}
func configureHelms(api *gin.Engine, data *DataLayer) {
api.GET("/api/helm/charts", func(c *gin.Context) {
res, err := data.ListInstalled()
if err != nil {
@@ -68,6 +73,21 @@ func configureRoutes(abortWeb ControlChan, data *DataLayer, api *gin.Engine) {
c.IndentedJSON(http.StatusOK, res)
})
api.DELETE("/api/helm/charts", func(c *gin.Context) {
cName := c.Query("chart")
cNamespace := c.Query("namespace")
if cName == "" {
_ = c.AbortWithError(http.StatusBadRequest, errors.New("missing required query string parameter: chart"))
return
}
err := data.UninstallChart(cNamespace, cName)
if err != nil {
_ = c.AbortWithError(http.StatusInternalServerError, err)
return
}
c.Redirect(http.StatusFound, "/")
})
api.GET("/api/helm/charts/history", func(c *gin.Context) {
cName := c.Query("chart")
cNamespace := c.Query("namespace")
@@ -105,8 +125,6 @@ func configureRoutes(abortWeb ControlChan, data *DataLayer, api *gin.Engine) {
c.IndentedJSON(http.StatusOK, res)
})
configureKubectls(api, data)
sections := map[string]SectionFn{
"manifests": data.RevisionManifests,
"values": data.RevisionValues,

View File

@@ -325,6 +325,14 @@ func (d *DataLayer) DescribeResource(namespace string, kind string, name string)
return out, nil
}
func (d *DataLayer) UninstallChart(namespace string, name string) error {
_, err := d.runCommandHelm("uninstall", name, "--namespace", namespace)
if err != nil {
return err
}
return nil
}
func RevisionDiff(functor SectionFn, ext string, namespace string, name string, revision1 int, revision2 int, flag bool) (string, error) {
if revision1 == 0 || revision2 == 0 {
log.Debugf("One of revisions is zero: %d %d", revision1, revision2)

View File

@@ -58,8 +58,12 @@
<div class="row mb-3">
</div>
<h1><span class="name"></span>,
revision <span class="rev"></span></h1>
<h1><span class="name"></span>, revision <span class="rev"></span>
<span class="float-end">
<a id="btnRollback" class="btn btn-sm bg-primary border border-secondary text-light" title="Rollback to this revision"><i class="fa fa-backward"></i> Rollback</a>
<a id="btnUninstall" class="btn btn-sm bg-danger border border-secondary text-light" title="Uninstall the chart"><i class="fa fa-trash"></i> Uninstall</a>
</span>
</h1>
Chart <b id="chartName"></b>: <i id="revDescr"></i>
<nav class="mt-2">
@@ -121,8 +125,8 @@
</div>
</div>
</div>
<div class="modal" id="describeModal"
tabindex="-1" aria-labelledby="describeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog modal-dialog-scrollable modal-xl">
@@ -138,6 +142,24 @@
</div>
</div>
<div class="modal" id="confirmModal"
tabindex="-1" aria-labelledby="describeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog modal-dialog-scrollable modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="confirmModalLabel"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="confirmModalBody">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary">Confirm</button>
</div>
</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>

View File

@@ -46,7 +46,7 @@ $("#nav-tab [data-tab]").click(function () {
} else {
const mode = getHashParam("mode")
if (!mode) {
$("#modePanel [data-mode=diff-prev]").trigger('click')
$("#modePanel [data-mode=view]").trigger('click')
} else {
$("#modePanel [data-mode=" + mode + "]").trigger('click')
}
@@ -137,7 +137,7 @@ function loadChartHistory(namespace, name) {
revRow.empty()
for (let x = 0; x < data.length; x++) {
const elm = data[x]
$("#specRev").val(elm.revision)
$("#specRev").val(elm.revision).data("last-rev", elm.revision)
const rev = $(`<div class="col-md-2 p-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/>
@@ -203,6 +203,41 @@ function setHashParam(name, val) {
window.location.hash = new URLSearchParams(params).toString()
}
function buildChartCard(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"><a href="#namespace=' + elm.namespace + '&chart=' + elm.name + '" class="link-dark" style="text-decoration: none">' + elm.name + '</a></h5>'))
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");
setHashParam("namespace", chart.namespace)
setHashParam("chart", chart.name)
loadChartHistory(chart.namespace, chart.name)
})
return card;
}
function loadChartsList() {
$("#sectionList").show()
chartsCards.empty().append("<div><i class='fa fa-spinner fa-spin fa-2x'></i> Loading...</div>")
@@ -211,38 +246,7 @@ function loadChartsList() {
}).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"><a href="#namespace=' + elm.namespace + '&chart=' + elm.name + '" class="link-dark" style="text-decoration: none">' + elm.name + '</a></h5>'))
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");
setHashParam("namespace", chart.namespace)
setHashParam("chart", chart.name)
loadChartHistory(chart.namespace, chart.name)
})
let card = buildChartCard(elm);
chartsCards.append($("<div class='col'></div>").append(card))
})
})
@@ -337,6 +341,8 @@ function showResources(namespace, chart, revision) {
badge.addClass("bg-success")
} else if (["Exists"].includes(data.status.phase)) {
badge.addClass("bg-success bg-opacity-50")
} else if (["Progressing"].includes(data.status.phase)) {
badge.addClass("bg-warning")
} else {
badge.addClass("bg-danger")
}
@@ -344,7 +350,7 @@ function showResources(namespace, chart, revision) {
const statusBlock = resBlock.find(".form-control.col-sm-4");
statusBlock.empty().append(badge).append("<span class='text-muted small'>" + (data.status.message ? data.status.message : '') + "</span>")
if (badge.text()!=="NotFound") {
if (badge.text() !== "NotFound") {
statusBlock.prepend("<i class=\"btn fa fa-search-plus float-end text-muted\"></i>")
statusBlock.find(".fa-search-plus").click(function () {
showDescribe(ns, res.kind, res.metadata.name)
@@ -378,3 +384,40 @@ function showDescribe(ns, kind, name) {
$("#describeModalBody").empty().append("<pre class='bg-white rounded p-3'></pre>").find("pre").html(data)
})
}
$("#btnUninstall").click(function () {
const chart = getHashParam('chart');
const namespace = getHashParam('namespace');
const revision = $("#specRev").data("last-rev")
$("#confirmModalLabel").html("Uninstall <b class='text-danger'>" + chart + "</b> from namespace <b class='text-danger'>" + namespace + "</b>")
$("#confirmModalBody").empty().append("<i class='fa fa-spin fa-spinner fa-2x'></i>")
$("#confirmModal .btn-primary").prop("disabled", true).off('click').click(function () {
$("#confirmModal .btn-primary").prop("disabled", true).append("<i class='fa fa-spin fa-spinner'></i>")
const url = "/api/helm/charts?namespace=" + namespace + "&chart=" + chart;
$.ajax({
url: url,
type: 'DELETE',
}).fail(function () {
reportError("Failed to delete the chart")
}).done(function () {
window.location.href = "/"
})
})
const myModal = new bootstrap.Modal(document.getElementById('confirmModal'), {});
myModal.show()
let qstr = "chart=" + chart + "&namespace=" + namespace + "&revision=" + revision
let url = "/api/helm/charts/resources"
url += "?" + qstr
$.getJSON(url).fail(function () {
reportError("Failed to get list of resources")
}).done(function (data) {
$("#confirmModalBody").empty().append("<p>Following resources will be deleted from the cluster:</p>");
$("#confirmModal .btn-primary").prop("disabled", false)
for (let i = 0; i < data.length; i++) {
const res = data[i]
$("#confirmModalBody").append("<p class='row'><i class='col-sm-3 text-end'>" + res.kind + "</i><b class='col-sm-9'>" + res.metadata.name + "</b></p>")
}
})
})