Bugfixes (#11)

* Don't offer rollback for the first revision

* Fix the rollback bug

* Cosmetics

* Errors shown as alerts
This commit is contained in:
Andrey Pokhilko
2022-09-15 13:27:24 +01:00
committed by GitHub
parent 269895ae31
commit 6b8d959491
5 changed files with 81 additions and 46 deletions

View File

@@ -11,18 +11,22 @@ Prerequisites: `helm` and `kubectl` binaries installed and operational.
Until we make our repo public, we have to use a custom way to install the plugin. Until we make our repo public, we have to use a custom way to install the plugin.
There is a need to build binary for plugin to function, run: There is a need to build binary for plugin to function, run:
```shell ```shell
go build -o bin/dashboard . go build -o bin/dashboard .
``` ```
To install, checkout the source code and run from source dir: To install, checkout the source code and run from source dir:
```shell ```shell
helm plugin install . helm plugin install .
``` ```
Local install of plugin just creates a symlink, so making the changes and rebuilding the binary would not require to reinstall a plugin. Local install of plugin just creates a symlink, so making the changes and rebuilding the binary would not require to
reinstall a plugin.
To use the plugin, run in your terminal: To use the plugin, run in your terminal:
```shell ```shell
helm dashboard helm dashboard
``` ```
@@ -32,43 +36,51 @@ Then, use the web UI.
## Uninstalling ## Uninstalling
To uninstall, run: To uninstall, run:
```shell ```shell
helm plugin uninstall dashboard helm plugin uninstall dashboard
``` ```
## Support Channels ## Support Channels
We have two main channels for supporting the tool users: [Slack community](#TODO) for general conversations and [GitHub issues](https://github.com/komodorio/helm-dashboard/issues) for real bugs. We have two main channels for supporting the Helm Dashboard users: [Slack community](#TODO) for general conversations
and [GitHub issues](https://github.com/komodorio/helm-dashboard/issues) for real bugs.
## Roadmap ## Roadmap
### Internal Milestone 1 ### First Public Version
- Helm Plugin Packaging - Helm Plugin Packaging
- CLI launcher - CLI launcher
- Web Server with REST API - Web Server with REST API
- Listing the installed applications
- View k8s resources created by the application (describe, status)
### First Public Version - Viewing revision history for application
Listing the installed applications - View manifest diffs between revisions, also changelogs etc
View k8s resources created by the application (describe, status) - Analytics reporting (telemetry)
Viewing revision history for application - Rollback to a revision
View manifest diffs between revisions, also changelogs etc - Check for repo updates & upgrade flow
Analytics reporting (telemetry) - Uninstalling the app completely
- Switch clusters
- Show manifest/describe upon clicking on resource
### Further Ideas ### Further Ideas
Setting parameter values and installing - Have cleaner idea on the web API structure
Installing new app from repo - Recognise & show ArgoCD-originating charts/objects, those `helm ls` does not show
Uninstalling the app completely - Recognise the revisions that are rollbacks by their description and mark in timeline
Reconfiguring the application
Rollback a revision
Validate manifests before deploy and get better errors #### Topic "Validating Manifests"
Switch clusters (?)
Browsing repositories
Adding new repository
Recognise & show ArgoCD-originating charts/objects - Validate manifests before deploy and get better errors
Have cleaner idea on the web API structure - See if we can build in Chechov or Validkube validation
See if we can build in Chechov or Validkube validation
Show manifest/describe upon clicking on resource #### Iteration "Value Setting"
Recognise the revisions that are rollbacks by their description and mark in timeline
- Setting parameter values and installing
- Reconfiguring the application
#### Iteration "Repo View"
- Browsing repositories
- Adding new repository
- Installing new app from repo

View File

@@ -43,13 +43,12 @@ func NewRouter(abortWeb ControlChan, data *DataLayer) *gin.Engine {
} }
api.Use(contextSetter(data)) api.Use(contextSetter(data))
api.Use(noCache)
api.Use(errorHandler)
configureStatic(api) configureStatic(api)
configureRoutes(abortWeb, data, api) configureRoutes(abortWeb, data, api)
api.Use(noCache)
api.Use(errorHandler)
return api return api
} }

View File

@@ -48,7 +48,7 @@ func (h *HelmHandler) Rollback(c *gin.Context) {
_ = c.AbortWithError(http.StatusInternalServerError, err) _ = c.AbortWithError(http.StatusInternalServerError, err)
return return
} }
c.Redirect(http.StatusOK, "/") c.Status(http.StatusAccepted)
} }
func (h *HelmHandler) History(c *gin.Context) { func (h *HelmHandler) History(c *gin.Context) {

View File

@@ -57,10 +57,10 @@
</div> </div>
<h1><span class="name"></span>, revision <span class="rev"></span> <h1><span class="name"></span>, revision <span class="rev"></span>
<span class="float-end" id="actionButtons"> <span class="float-end" id="actionButtons">
<a id="btnUpgrade" class="opacity-10 btn btn-sm bg-secondary text-light bg-opacity-50 rounded-0 me-0 rounded-start ">Checking...</a><a id="btnUpgradeCheck" class="btn btn-sm text-muted btn-light border-secondary rounded-0 rounded-end ms-0" title="Check for newer chart version from repo"><i class="bi-repeat"></i><span class="spinner-border spinner-border-sm" style="display: none" role="status" aria-hidden="true"></span></a> <button id="btnUpgrade" class="opacity-10 btn btn-sm bg-secondary text-light bg-opacity-50 rounded-0 me-0 rounded-start ">Checking...</button><button id="btnUpgradeCheck" class="btn btn-sm text-muted btn-light border-secondary rounded-0 rounded-end ms-0" title="Check for newer chart version from repo"><i class="bi-repeat"></i><span class="spinner-border spinner-border-sm" style="display: none" role="status" aria-hidden="true"></span></button>
<a id="btnRollback" class="btn btn-sm bg-primary border border-secondary text-light" title="Rollback to this revision"><i class="bi-rewind-fill"></i> <span>Rollback</span></a> <button id="btnRollback" class="btn btn-sm bg-primary border border-secondary text-light" title="Rollback to this revision"><i class="bi-rewind-fill"></i> <span>Rollback</span></button>
<a id="btnUninstall" class="btn btn-sm bg-danger border border-secondary text-light" title="Uninstall the chart"><i class="bi-trash-fill"></i> Uninstall</a> <button id="btnUninstall" class="btn btn-sm bg-danger border border-secondary text-light" title="Uninstall the chart"><i class="bi-trash-fill"></i> Uninstall</button>
</span> </span>
</h1> </h1>
Chart <b id="chartName"></b>: <i id="revDescr"></i> Chart <b id="chartName"></b>: <i id="revDescr"></i>
@@ -124,6 +124,13 @@
</div> </div>
</div> </div>
<div id="errorAlert" 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>
<hr>
<p style="white-space: pre-wrap"></p>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
</div> </div>
<div class="modal" id="describeModal" <div class="modal" id="describeModal"
@@ -179,6 +186,8 @@
</div> </div>
</div> </div>
</div> </div>
<footer class="text-center small mt-3"> <footer class="text-center small mt-3">
Brought to you by <img src="https://komodor.com/wp-content/uploads/2021/05/favicon.png" style="height: 1rem"> <a href="https://komodor.io">Komodor.io</a> | Brought to you by <img src="https://komodor.com/wp-content/uploads/2021/05/favicon.png" style="height: 1rem"> <a href="https://komodor.io">Komodor.io</a> |
<i class="bi-github"></i> <i class="bi-github"></i>

View File

@@ -2,8 +2,10 @@ const clusterSelect = $("#cluster");
const chartsCards = $("#charts"); const chartsCards = $("#charts");
const revRow = $("#sectionDetails .row"); const revRow = $("#sectionDetails .row");
function reportError(err) { function reportError(err, xhr) {
alert(err) // TODO: nice modal/baloon/etc $("#errorAlert h4 span").text(err)
$("#errorAlert p").text(xhr.responseText)
$("#errorAlert").show()
} }
function revisionClicked(namespace, name, self) { function revisionClicked(namespace, name, self) {
@@ -20,11 +22,12 @@ function revisionClicked(namespace, name, self) {
$("#revDescr").addClass("text-danger") $("#revDescr").addClass("text-danger")
} }
if (false) { // TODO: hide if only one revision const rev = $("#specRev").data("last-rev") == elm.revision ? elm.revision - 1 : elm.revision
console.log(rev, $("#specRev").data("first-rev"))
if (!rev || getHashParam("revision") === $("#specRev").data("first-rev")) {
$("#btnRollback").hide() $("#btnRollback").hide()
} else { } else {
const rev = $("#specRev").data("last-rev") == elm.revision ? elm.revision - 1 : elm.revision $("#btnRollback").show().data("rev", rev).find("span").text("Rollback to #" + rev)
$("#btnRollback").data("rev", rev).show().find("span").text("Rollback to #" + rev)
} }
const tab = getHashParam("tab") const tab = getHashParam("tab")
@@ -78,11 +81,14 @@ $("#userDefinedVals").change(function () {
function loadContentWrapper() { function loadContentWrapper() {
let revDiff = 0 let revDiff = 0
const revision = parseInt(getHashParam("revision")); const revision = parseInt(getHashParam("revision"));
if (getHashParam("mode") === "diff-prev") { if (revision === $("#specRev").data("first-rev")) {
revDiff = 0
} else if (getHashParam("mode") === "diff-prev") {
revDiff = revision - 1 revDiff = revision - 1
} else if (getHashParam("mode") === "diff-rev") { } else if (getHashParam("mode") === "diff-rev") {
revDiff = $("#specRev").val() revDiff = $("#specRev").val()
} }
const flag = $("#userDefinedVals").prop("checked"); const flag = $("#userDefinedVals").prop("checked");
loadContent(getHashParam("tab"), getHashParam("namespace"), getHashParam("chart"), revision, revDiff, flag) loadContent(getHashParam("tab"), getHashParam("namespace"), getHashParam("chart"), revision, revDiff, flag)
} }
@@ -139,6 +145,11 @@ function fillChartHistory(data, namespace, name) {
for (let x = 0; x < data.length; x++) { for (let x = 0; x < data.length; x++) {
const elm = data[x] const elm = data[x]
$("#specRev").val(elm.revision).data("last-rev", elm.revision).data("last-chart-ver", elm.chart_ver) $("#specRev").val(elm.revision).data("last-rev", elm.revision).data("last-chart-ver", elm.chart_ver)
if (!x) {
$("#specRev").data("first-rev", elm.revision)
}
const rev = $(`<div class="col-md-2 p-2 rounded border border-secondary bg-gradient bg-white"> 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><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">Chart:</span> <span class="chart-ver"></span><br/>
@@ -244,7 +255,7 @@ function checkUpgradeable(name) {
const canUpgrade = isNewerVersion(verCur, elm.version); const canUpgrade = isNewerVersion(verCur, elm.version);
$("#btnUpgradeCheck").prop("disabled", false) $("#btnUpgradeCheck").prop("disabled", false)
if (canUpgrade) { if (canUpgrade) {
$("#btnUpgrade").removeClass("bg-secondary bg-opacity-50").addClass("bg-success").text("Upgrade to "+elm.version) $("#btnUpgrade").removeClass("bg-secondary bg-opacity-50").addClass("bg-success").text("Upgrade to " + elm.version)
} else { } else {
$("#btnUpgrade").removeClass("bg-success").addClass("bg-secondary bg-opacity-50").text("No upgrades") $("#btnUpgrade").removeClass("bg-success").addClass("bg-secondary bg-opacity-50").text("No upgrades")
} }
@@ -281,7 +292,7 @@ $('#upgradeModalLabel select').change(function () {
}) })
$("#upgradeModal .btn-secondary").click(function () { $("#upgradeModal .btn-secondary").click(function () {
const self=$(this) const self = $(this)
self.find(".fa").removeClass("fa-cloud-download").addClass("fa-spin fa-spinner").prop("disabled", true) self.find(".fa").removeClass("fa-cloud-download").addClass("fa-spin fa-spinner").prop("disabled", true)
$("#btnUpgradeCheck").click() $("#btnUpgradeCheck").click()
$("#upgradeModal .btn-close").click() $("#upgradeModal .btn-close").click()
@@ -365,8 +376,8 @@ function buildChartCard(elm) {
function loadChartsList() { function loadChartsList() {
$("#sectionList").show() $("#sectionList").show()
chartsCards.empty().append("<div><span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span> Loading...</div>") chartsCards.empty().append("<div><span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span> Loading...</div>")
$.getJSON("/api/helm/charts").fail(function () { $.getJSON("/api/helm/charts").fail(function (xhr) {
reportError("Failed to get list of charts") reportError("Failed to get list of charts", xhr)
}).done(function (data) { }).done(function (data) {
chartsCards.empty() chartsCards.empty()
data.forEach(function (elm) { data.forEach(function (elm) {
@@ -558,8 +569,8 @@ $("#btnRollback").click(function () {
$.ajax({ $.ajax({
url: url, url: url,
type: 'POST', type: 'POST',
}).fail(function () { }).fail(function (xhr) {
reportError("Failed to rollback the chart") reportError("Failed to rollback the chart", xhr)
}).done(function () { }).done(function () {
window.location.reload() window.location.reload()
}) })
@@ -584,7 +595,11 @@ $("#btnRollback").click(function () {
}; };
const diff2htmlUi = new Diff2HtmlUI(targetElement, data, configuration); const diff2htmlUi = new Diff2HtmlUI(targetElement, data, configuration);
diff2htmlUi.draw() diff2htmlUi.draw()
if (data) {
$("#confirmModalBody").prepend("<p>Following changes will happen to cluster:</p>") $("#confirmModalBody").prepend("<p>Following changes will happen to cluster:</p>")
} else {
$("#confirmModalBody").html("<p>No changes will happen to cluster</p>")
}
}) })
}) })