Allow installing into cluster (#128)

* Dockerize it

* Default chart layout

* Installable

* Starts and loads

* Progressing

* Hide cluster block

* Add scanners

* Add icon for helm chart (#130)

Co-authored-by: Harshit Mehta <harshitm@nvidia.com>

* Image build and push scripts

* Build local img

* Local img

* ci stuff

* ci and chart changes

* add readme

* modify readme

* move readme location

* update docs and delete file

* remove file

* allow write actions

* allow write actions

* update .gitignore

* update readme

* delete file

* add persistence and update documentation

* update logo

* update volume paths and documentation

* change pvc size

* Comment

Co-authored-by: Harshit Mehta <hdm23061993@gmail.com>
Co-authored-by: Harshit Mehta <harshitm@nvidia.com>
Co-authored-by: ronahk <rona@komodor.io>
This commit is contained in:
Andrey Pokhilko
2022-12-18 14:09:07 +02:00
committed by GitHub
parent 9bac7306a4
commit f6d3e519e2
30 changed files with 829 additions and 50 deletions

View File

@@ -29,6 +29,11 @@ func (s Server) StartServer() (string, utils.ControlChan) {
data := subproc.DataLayer{
Namespace: s.Namespace,
Cache: subproc.NewCache(),
StatusInfo: &subproc.StatusInfo{
CurVer: s.Version,
Analytics: false,
LimitedToNamespace: s.Namespace,
},
}
err := data.CheckConnectivity()
if err != nil {
@@ -36,12 +41,7 @@ func (s Server) StartServer() (string, utils.ControlChan) {
os.Exit(1) // TODO: propagate error instead?
}
isDevModeWithAnalytics := os.Getenv("HD_DEV_ANALYTICS") == "true"
enableAnalytics := (!s.NoTracking && s.Version != "0.0.0") || isDevModeWithAnalytics
data.StatusInfo = &subproc.StatusInfo{
CurVer: s.Version,
Analytics: enableAnalytics,
LimitedToNamespace: s.Namespace,
}
data.StatusInfo.Analytics = (!s.NoTracking && s.Version != "0.0.0") || isDevModeWithAnalytics
go checkUpgrade(data.StatusInfo)
discoverScanners(&data)
@@ -113,7 +113,7 @@ func discoverScanners(data *subproc.DataLayer) {
}
}
func checkUpgrade(d *subproc.StatusInfo) {
func checkUpgrade(d *subproc.StatusInfo) { // TODO: check it once an hour
url := "https://api.github.com/repos/komodorio/helm-dashboard/releases/latest"
type GHRelease struct {
Name string `json:"name"`

View File

@@ -13,7 +13,11 @@ function revisionClicked(namespace, name, self) {
$("#sectionDetails .rev-tags .rev-chart").text(elm.chart)
$("#sectionDetails .rev-tags .rev-app").text(elm.app_version)
$("#sectionDetails .rev-tags .rev-ns").text(getHashParam("namespace"))
$("#sectionDetails .rev-tags .rev-cluster").text(getHashParam("context"))
if (getHashParam("context")) {
$("#sectionDetails .rev-tags .rev-cluster").text(getHashParam("context"))
} else {
$("#sectionDetails .rev-tags .rev-cluster").parent().hide() // TODO: makes UI jumpy, change to showing
}
$("#revDescr").text(elm.description).removeClass("text-danger")
if (elm.status === "failed") {
@@ -158,7 +162,9 @@ function showResources(namespace, chart, revision) {
}
resBody.empty();
data = data.sort(function(a, b){return interestingResources.indexOf(a.kind.toUpperCase()) - interestingResources.indexOf(b.kind.toUpperCase())}).reverse();
data = data.sort(function (a, b) {
return interestingResources.indexOf(a.kind.toUpperCase()) - interestingResources.indexOf(b.kind.toUpperCase())
}).reverse();
for (let i = 0; i < data.length; i++) {
const res = data[i]
const resBlock = $(`

View File

@@ -75,24 +75,6 @@
</div>
<div class="separator-vertical"><span></span></div>
<i class="btn bi-power text-muted p-2 m-1 mx-2" title="Shut down the Helm Dashboard application"></i>
<!-- Modal -->
<div class="modal fade" id="PowerOffModal" tabindex="-1" aria-labelledby="ModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ModalLabel">Message</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Close this browser tab now.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
</nav>
<!-- /TOP BAR -->
@@ -144,9 +126,11 @@
<!-- FILTER BLOCK -->
<div class="p-2 ps-2 bg-white rounded-1 b-shadow" id="filters">
<form>
<h4>Clusters</h4>
<ul class="list-unstyled" id="cluster">
</ul>
<div id="clusterFilterBlock">
<h4>Clusters</h4>
<ul class="list-unstyled" id="cluster">
</ul>
</div>
<h4 class="mt-4">Namespaces</h4>
<p id="limitNamespace" class="display-none ps-3"><span class="fw-bold"></span> (forced)</p>
@@ -165,7 +149,8 @@
<h2 class="m-0 p-1"><img class="m-2 mx-3 me-2" src="static/helm-gray.svg" alt="Installed Charts">Installed
Charts (<span></span>)</h2>
<div class="form-outline w-25">
<input type="text" id="installedSearch" class="form-control form-control-sm" placeholder="Filter..." />
<input type="text" id="installedSearch" class="form-control form-control-sm"
placeholder="Filter..."/>
</div>
</div>
<div class="bg-secondary rounded-bottom m-0 row p-2">
@@ -182,7 +167,8 @@
<div class="bg-white rounded shadow p-3 display-none no-charts">Looks like you don't have any charts
installed. "Repository" section may be a good place to start.
</div>
<div class="bg-white rounded shadow p-3 display-none all-filtered">There are no releases matching your filter criteria. Reset your filters or install more charts.
<div class="bg-white rounded shadow p-3 display-none all-filtered">There are no releases matching your
filter criteria. Reset your filters or install more charts.
</div>
</div>
<!-- /INSTALLED LIST -->
@@ -430,6 +416,21 @@
</div>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="PowerOffModal" tabindex="-1" aria-labelledby="ModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ModalLabel">Session Ended</h5>
</div>
<div class="modal-body">
The Helm Dashboard application has been shut down. You can now close the browser tab.
</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

@@ -132,7 +132,8 @@ function repoChartClicked() {
window.location.reload()
} else {
const contexts = $("body").data("contexts")
const contextNamespace = contexts.filter(obj => {return obj.Name === getHashParam("context")})[0].Namespace
const ctxFiltered = contexts.filter(obj => {return obj.Name === getHashParam("context")});
const contextNamespace = ctxFiltered.length?ctxFiltered[0].Namespace:""
popUpUpgrade(elm, contextNamespace)
}
}

View File

@@ -3,12 +3,18 @@ $(function () {
$.getJSON("/status").fail(function (xhr) { // maybe /options call in the future
reportError("Failed to get tool version", xhr)
}).done(function (data) {
$("body").data("status", data)
fillToolVersion(data)
limNS = data.LimitedToNamespace
if (limNS) {
$("#limitNamespace").show().find("span").text(limNS)
}
fillClusters(limNS)
if (data.ClusterMode) {
$(".bi-power").hide()
$("#clusterFilterBlock").hide()
}
})
$.getJSON("/api/scanners").fail(function (xhr) {
@@ -47,7 +53,7 @@ function fillClusters(limNS) {
const context = getHashParam("context")
data.sort((a, b) => (getCleanClusterName(a.Name) > getCleanClusterName(b.Name)) - (getCleanClusterName(a.Name) < getCleanClusterName(b.Name)))
fillClusterList(data, context);
sendStats('contexts', {'status': 'success', length:data.length});
sendStats('contexts', {'status': 'success', length: data.length});
$.getJSON("/api/kube/namespaces").fail(function (xhr) {
reportError("Failed to get namespaces", xhr)
}).done(function (res) {
@@ -220,7 +226,7 @@ function fillNamespaceList(data) {
if (filteredNamespace.split('+').includes(elm.metadata.name)) {
opt.find("input").prop("checked", true)
}
} else if (curContextNamespaces && curContextNamespaces[0].Namespace === elm.metadata.name) {
} else if (curContextNamespaces.length && curContextNamespaces[0].Namespace === elm.metadata.name) {
opt.find("input").prop("checked", true)
setFilteredNamespaces([elm.metadata.name])
}

View File

@@ -35,6 +35,7 @@ type StatusInfo struct {
Analytics bool
LimitedToNamespace string
CacheHitRatio float64
ClusterMode bool
}
func (d *DataLayer) runCommand(cmd ...string) (string, error) {
@@ -74,10 +75,17 @@ func (d *DataLayer) CheckConnectivity() error {
}
if len(contexts) < 1 {
return errors.New("did not find any kubectl contexts configured")
log.Debugf("Did not find any contexts, will try checking k8s")
_, err := d.runCommandKubectl("get", "pods")
if err != nil {
log.Debugf("The error were: %s", err)
return errors.New("did not find any kubectl contexts configured")
}
log.Infof("Assuming k8s environment")
d.StatusInfo.ClusterMode = true
}
_, err = d.runCommandHelm("--help") // no point in doing is, since the default context may be invalid
_, err = d.runCommandHelm("--help")
if err != nil {
return err
}

View File

@@ -3,6 +3,7 @@ package subproc
import (
"encoding/json"
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
"os"
"regexp"
"sort"
"strings"
@@ -31,6 +32,12 @@ type KubeContext struct {
}
func (d *DataLayer) ListContexts() (res []KubeContext, err error) {
res = []KubeContext{}
if os.Getenv("HD_CLUSTER_MODE") != "" {
return res, nil
}
out, err := d.runCommandKubectl("config", "get-contexts")
if err != nil {
return nil, err

View File

@@ -12,6 +12,7 @@ import (
func (d *DataLayer) ChartRepoList() (res []RepositoryElement, err error) {
out, err := d.Cache.String(CacheKeyAllRepos, nil, func() (string, error) {
// TODO: do a bg check, if the state is changed - do reset some caches
return d.runCommandHelm("repo", "list", "--output", "json")
})
if err != nil {