mirror of
https://github.com/komodorio/helm-dashboard.git
synced 2026-03-24 11:48:04 +00:00
Bugfixes (#11)
* Don't offer rollback for the first revision * Fix the rollback bug * Cosmetics * Errors shown as alerts
This commit is contained in:
64
README.md
64
README.md
@@ -11,64 +11,76 @@ 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
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, use the web UI.
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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()
|
||||||
$("#confirmModalBody").prepend("<p>Following changes will happen to cluster:</p>")
|
if (data) {
|
||||||
|
$("#confirmModalBody").prepend("<p>Following changes will happen to cluster:</p>")
|
||||||
|
} else {
|
||||||
|
$("#confirmModalBody").html("<p>No changes will happen to cluster</p>")
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user