mirror of
https://github.com/komodorio/helm-dashboard.git
synced 2026-03-24 03:38:04 +00:00
Resource status display (#6)
* Start experiments * Going to get resources * Start to show resources * Working on statuses * Displaying statuses * Fix complexity?
This commit is contained in:
@@ -5,6 +5,8 @@ import (
|
||||
"errors"
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v12 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
@@ -66,15 +68,6 @@ func configureRoutes(abortWeb ControlChan, data *DataLayer, api *gin.Engine) {
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
})
|
||||
|
||||
api.GET("/api/kube/contexts", func(c *gin.Context) {
|
||||
res, err := data.ListContexts()
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
})
|
||||
|
||||
api.GET("/api/helm/charts/history", func(c *gin.Context) {
|
||||
cName := c.Query("chart")
|
||||
cNamespace := c.Query("namespace")
|
||||
@@ -91,6 +84,29 @@ func configureRoutes(abortWeb ControlChan, data *DataLayer, api *gin.Engine) {
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
})
|
||||
|
||||
api.GET("/api/helm/charts/resources", 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
|
||||
}
|
||||
cRev, err := strconv.Atoi(c.Query("revision"))
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := data.RevisionManifestsParsed(cNamespace, cName, cRev)
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
})
|
||||
|
||||
configureKubectls(api, data)
|
||||
|
||||
sections := map[string]SectionFn{
|
||||
"manifests": data.RevisionManifests,
|
||||
"values": data.RevisionValues,
|
||||
@@ -147,6 +163,50 @@ func configureRoutes(abortWeb ControlChan, data *DataLayer, api *gin.Engine) {
|
||||
})
|
||||
}
|
||||
|
||||
func configureKubectls(api *gin.Engine, data *DataLayer) {
|
||||
api.GET("/api/kube/contexts", func(c *gin.Context) {
|
||||
res, err := data.ListContexts()
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
})
|
||||
|
||||
api.GET("/api/kube/resources/:kind", func(c *gin.Context) {
|
||||
cName := c.Query("name")
|
||||
cNamespace := c.Query("namespace")
|
||||
if cName == "" {
|
||||
_ = c.AbortWithError(http.StatusBadRequest, errors.New("missing required query string parameter: name"))
|
||||
return
|
||||
}
|
||||
|
||||
res, err := data.GetResource(cNamespace, &GenericResource{
|
||||
TypeMeta: v1.TypeMeta{Kind: c.Param("kind")},
|
||||
ObjectMeta: v1.ObjectMeta{Name: cName},
|
||||
})
|
||||
if err != nil {
|
||||
_ = c.AbortWithError(http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
if res.Status.Phase == "Active" || res.Status.Phase == "Error" {
|
||||
_ = res.Name + ""
|
||||
} else if res.Status.Phase == "" && len(res.Status.Conditions) > 0 {
|
||||
res.Status.Phase = v12.CarpPhase(res.Status.Conditions[len(res.Status.Conditions)-1].Type)
|
||||
res.Status.Message = res.Status.Conditions[len(res.Status.Conditions)-1].Message
|
||||
res.Status.Reason = res.Status.Conditions[len(res.Status.Conditions)-1].Reason
|
||||
if res.Status.Conditions[len(res.Status.Conditions)-1].Status == "False" {
|
||||
res.Status.Phase = "Not" + res.Status.Phase
|
||||
}
|
||||
} else if res.Status.Phase == "" {
|
||||
res.Status.Phase = "Exists"
|
||||
}
|
||||
|
||||
c.IndentedJSON(http.StatusOK, res)
|
||||
})
|
||||
}
|
||||
|
||||
func configureStatic(api *gin.Engine) {
|
||||
fs := http.FS(staticFS)
|
||||
|
||||
|
||||
@@ -10,9 +10,12 @@ import (
|
||||
"github.com/hexops/gotextdiff/myers"
|
||||
"github.com/hexops/gotextdiff/span"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -24,11 +27,11 @@ type DataLayer struct {
|
||||
Kubectl string
|
||||
}
|
||||
|
||||
func (l *DataLayer) runCommand(cmd ...string) (string, error) {
|
||||
func (d *DataLayer) runCommand(cmd ...string) (string, error) {
|
||||
log.Debugf("Starting command: %s", cmd)
|
||||
prog := exec.Command(cmd[0], cmd[1:]...)
|
||||
prog.Env = os.Environ()
|
||||
prog.Env = append(prog.Env, "HELM_KUBECONTEXT="+l.KubeContext)
|
||||
prog.Env = append(prog.Env, "HELM_KUBECONTEXT="+d.KubeContext)
|
||||
|
||||
var stdout bytes.Buffer
|
||||
prog.Stdout = &stdout
|
||||
@@ -55,35 +58,36 @@ func (l *DataLayer) runCommand(cmd ...string) (string, error) {
|
||||
return string(sout), nil
|
||||
}
|
||||
|
||||
func (l *DataLayer) runCommandHelm(cmd ...string) (string, error) {
|
||||
if l.Helm == "" {
|
||||
l.Helm = "helm"
|
||||
func (d *DataLayer) runCommandHelm(cmd ...string) (string, error) {
|
||||
if d.Helm == "" {
|
||||
d.Helm = "helm"
|
||||
}
|
||||
|
||||
cmd = append([]string{l.Helm}, cmd...)
|
||||
if l.KubeContext != "" {
|
||||
cmd = append(cmd, "--kube-context", l.KubeContext)
|
||||
cmd = append([]string{d.Helm}, cmd...)
|
||||
if d.KubeContext != "" {
|
||||
cmd = append(cmd, "--kube-context", d.KubeContext)
|
||||
}
|
||||
|
||||
return l.runCommand(cmd...)
|
||||
return d.runCommand(cmd...)
|
||||
}
|
||||
|
||||
func (l *DataLayer) runCommandKubectl(cmd ...string) (string, error) {
|
||||
if l.Kubectl == "" {
|
||||
l.Kubectl = "kubectl"
|
||||
func (d *DataLayer) runCommandKubectl(cmd ...string) (string, error) {
|
||||
// TODO: migrate into using kubectl "k8s.io/kubectl/pkg/cmd" and kube API
|
||||
if d.Kubectl == "" {
|
||||
d.Kubectl = "kubectl"
|
||||
}
|
||||
|
||||
cmd = append([]string{l.Kubectl}, cmd...)
|
||||
cmd = append([]string{d.Kubectl}, cmd...)
|
||||
|
||||
if l.KubeContext != "" {
|
||||
cmd = append(cmd, "--context", l.KubeContext)
|
||||
if d.KubeContext != "" {
|
||||
cmd = append(cmd, "--context", d.KubeContext)
|
||||
}
|
||||
|
||||
return l.runCommand(cmd...)
|
||||
return d.runCommand(cmd...)
|
||||
}
|
||||
|
||||
func (l *DataLayer) CheckConnectivity() error {
|
||||
contexts, err := l.ListContexts()
|
||||
func (d *DataLayer) CheckConnectivity() error {
|
||||
contexts, err := d.ListContexts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -93,7 +97,7 @@ func (l *DataLayer) CheckConnectivity() error {
|
||||
}
|
||||
|
||||
/*
|
||||
_, err = l.runCommandHelm("env") // no point in doing is, since the default context may be invalid
|
||||
_, err = d.runCommandHelm("env") // no point in doing is, since the default context may be invalid
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -110,8 +114,8 @@ type KubeContext struct {
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func (l *DataLayer) ListContexts() (res []KubeContext, err error) {
|
||||
out, err := l.runCommandKubectl("config", "get-contexts")
|
||||
func (d *DataLayer) ListContexts() (res []KubeContext, err error) {
|
||||
out, err := d.runCommandKubectl("config", "get-contexts")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -144,8 +148,8 @@ func (l *DataLayer) ListContexts() (res []KubeContext, err error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (l *DataLayer) ListInstalled() (res []releaseElement, err error) {
|
||||
out, err := l.runCommandHelm("ls", "--all", "--all-namespaces", "--output", "json", "--time-format", time.RFC3339)
|
||||
func (d *DataLayer) ListInstalled() (res []releaseElement, err error) {
|
||||
out, err := d.runCommandHelm("ls", "--all", "--all-namespaces", "--output", "json", "--time-format", time.RFC3339)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -157,9 +161,9 @@ func (l *DataLayer) ListInstalled() (res []releaseElement, err error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (l *DataLayer) ChartHistory(namespace string, chartName string) (res []*historyElement, err error) {
|
||||
func (d *DataLayer) ChartHistory(namespace string, chartName string) (res []*historyElement, err error) {
|
||||
// TODO: there is `max` but there is no `offset`
|
||||
out, err := l.runCommandHelm("history", chartName, "--namespace", namespace, "--output", "json", "--max", "18")
|
||||
out, err := d.runCommandHelm("history", chartName, "--namespace", namespace, "--output", "json", "--max", "18")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -209,8 +213,8 @@ func (l *DataLayer) ChartHistory(namespace string, chartName string) (res []*his
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (l *DataLayer) ChartRepoVersions(chartName string) (res []repoChartElement, err error) {
|
||||
out, err := l.runCommandHelm("search", "repo", "--regexp", "/"+chartName+"\v", "--versions", "--output", "json")
|
||||
func (d *DataLayer) ChartRepoVersions(chartName string) (res []repoChartElement, err error) {
|
||||
out, err := d.runCommandHelm("search", "repo", "--regexp", "/"+chartName+"\v", "--versions", "--output", "json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -222,36 +226,97 @@ func (l *DataLayer) ChartRepoVersions(chartName string) (res []repoChartElement,
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type SectionFn = func(string, string, int, bool) (string, error)
|
||||
type SectionFn = func(string, string, int, bool) (string, error) // TODO: rework it into struct-based argument?
|
||||
|
||||
func (l *DataLayer) RevisionManifests(namespace string, chartName string, revision int, _ bool) (res string, err error) {
|
||||
out, err := l.runCommandHelm("get", "manifest", chartName, "--namespace", namespace, "--revision", strconv.Itoa(revision))
|
||||
func (d *DataLayer) RevisionManifests(namespace string, chartName string, revision int, _ bool) (res string, err error) {
|
||||
out, err := d.runCommandHelm("get", "manifest", chartName, "--namespace", namespace, "--revision", strconv.Itoa(revision))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (l *DataLayer) RevisionNotes(namespace string, chartName string, revision int, _ bool) (res string, err error) {
|
||||
out, err := l.runCommandHelm("get", "notes", chartName, "--namespace", namespace, "--revision", strconv.Itoa(revision))
|
||||
func (d *DataLayer) RevisionManifestsParsed(namespace string, chartName string, revision int) ([]*GenericResource, error) {
|
||||
out, err := d.RevisionManifests(namespace, chartName, revision, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dec := yaml.NewDecoder(bytes.NewReader([]byte(out)))
|
||||
|
||||
res := make([]*GenericResource, 0)
|
||||
var tmp interface{}
|
||||
for dec.Decode(&tmp) == nil {
|
||||
// k8s libs uses only JSON tags defined, say hello to https://github.com/go-yaml/yaml/issues/424
|
||||
// bug we can juggle it
|
||||
jsoned, err := json.Marshal(tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var doc GenericResource
|
||||
err = json.Unmarshal(jsoned, &doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = append(res, &doc)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) RevisionNotes(namespace string, chartName string, revision int, _ bool) (res string, err error) {
|
||||
out, err := d.runCommandHelm("get", "notes", chartName, "--namespace", namespace, "--revision", strconv.Itoa(revision))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (l *DataLayer) RevisionValues(namespace string, chartName string, revision int, onlyUserDefined bool) (res string, err error) {
|
||||
func (d *DataLayer) RevisionValues(namespace string, chartName string, revision int, onlyUserDefined bool) (res string, err error) {
|
||||
cmd := []string{"get", "values", chartName, "--namespace", namespace, "--revision", strconv.Itoa(revision), "--output", "yaml"}
|
||||
if !onlyUserDefined {
|
||||
cmd = append(cmd, "--all")
|
||||
}
|
||||
out, err := l.runCommandHelm(cmd...)
|
||||
out, err := d.runCommandHelm(cmd...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (d *DataLayer) GetResource(namespace string, def *GenericResource) (*GenericResource, error) {
|
||||
out, err := d.runCommandKubectl("get", strings.ToLower(def.Kind), def.Name, "--namespace", namespace, "--output", "json")
|
||||
if err != nil {
|
||||
if strings.HasSuffix(strings.TrimSpace(err.Error()), " not found") {
|
||||
return &GenericResource{
|
||||
Status: v1.CarpStatus{
|
||||
Phase: "NotFound",
|
||||
Message: err.Error(),
|
||||
Reason: "not found",
|
||||
},
|
||||
}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var res GenericResource
|
||||
err = json.Unmarshal([]byte(out), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Slice(res.Status.Conditions, func(i, j int) bool {
|
||||
t1 := res.Status.Conditions[i].LastTransitionTime
|
||||
t2 := res.Status.Conditions[j].LastTransitionTime
|
||||
return t1.Time.Before(t2.Time)
|
||||
})
|
||||
|
||||
return &res, 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)
|
||||
@@ -274,3 +339,5 @@ func RevisionDiff(functor SectionFn, ext string, namespace string, name string,
|
||||
log.Debugf("The diff is: %s", diff)
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
type GenericResource = v1.Carp
|
||||
|
||||
@@ -3,6 +3,7 @@ package dashboard
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -55,12 +56,28 @@ func TestFlow(t *testing.T) {
|
||||
}
|
||||
_ = upgrade
|
||||
|
||||
manifests, err := data.RevisionManifests(chart.Namespace, chart.Name, history[len(history)-1].Revision, true)
|
||||
manifests, err := data.RevisionManifestsParsed(chart.Namespace, chart.Name, history[len(history)-1].Revision)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = manifests
|
||||
|
||||
var wg sync.WaitGroup
|
||||
res := make([]*GenericResource, 0)
|
||||
for _, m := range manifests {
|
||||
wg.Add(1)
|
||||
mc := m // fix the clojure
|
||||
func() {
|
||||
defer wg.Done()
|
||||
lst, err := data.GetResource(chart.Namespace, mc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res = append(res, lst)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
diff, err := RevisionDiff(data.RevisionManifests, ".yaml", chart.Namespace, chart.Name, history[len(history)-1].Revision, history[len(history)-2].Revision, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/lightfair.min.css" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/lightfair.min.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css"/>
|
||||
<link href="static/styles.css" rel="stylesheet">
|
||||
</head>
|
||||
@@ -63,14 +63,12 @@
|
||||
|
||||
<nav class="mt-2">
|
||||
<div class="nav nav-tabs" id="nav-tab" role="tablist">
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nav-resources" data-tab="resources"
|
||||
type="button" role="tab" aria-controls="nav-resources" aria-selected="false">Resources
|
||||
</button>
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nav-manifest" data-tab="manifests"
|
||||
type="button" role="tab" aria-controls="nav-manifest-diff" aria-selected="false">Manifests
|
||||
</button>
|
||||
<!-- TODO
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nav-disabled" data-tab="resources"
|
||||
type="button" role="tab" aria-controls="nav-disabled" aria-selected="false" disabled>Resources
|
||||
</button>
|
||||
-->
|
||||
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#nav-manifest" data-tab="values"
|
||||
type="button" role="tab" aria-controls="nav-disabled" aria-selected="false">
|
||||
Parameterized Values
|
||||
@@ -80,21 +78,26 @@
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
<div class="tab-pane" id="nav-manifest" role="tabpanel"
|
||||
tabindex="0">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane p-3" id="nav-resources" role="tabpanel">
|
||||
|
||||
</div>
|
||||
<div class="tab-pane" id="nav-manifest" role="tabpanel">
|
||||
<nav class="navbar bg-light">
|
||||
<form class="container-fluid" id="modePanel">
|
||||
<label class="form-check-label" for="diffModeNone">
|
||||
<input class="form-check-input" type="radio" name="diffMode" id="diffModeNone" data-mode="view">
|
||||
<input class="form-check-input" type="radio" name="diffMode" id="diffModeNone"
|
||||
data-mode="view">
|
||||
View Current
|
||||
</label>
|
||||
<label class="form-check-label" for="diffModePrev">
|
||||
<input class="form-check-input" type="radio" name="diffMode" id="diffModePrev" data-mode="diff-prev">
|
||||
<input class="form-check-input" type="radio" name="diffMode" id="diffModePrev"
|
||||
data-mode="diff-prev">
|
||||
Diff with previous
|
||||
</label>
|
||||
<label class="form-check-label" for="diffModeRev">
|
||||
<input class="form-check-input" type="radio" name="diffMode" id="diffModeRev" data-mode="diff-rev">
|
||||
<input class="form-check-input" type="radio" name="diffMode" id="diffModeRev"
|
||||
data-mode="diff-rev">
|
||||
Diff with specific revision: <input class="form-input" size="3" id="specRev">
|
||||
</label>
|
||||
<label class="form-check-label" for="userDefinedVals">
|
||||
@@ -126,10 +129,13 @@
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js" integrity="sha512-gU7kztaQEl7SHJyraPfZLQCNnrKdaQi5ndOyt4L4UPL/FHDd/uB9Je6KDARIqwnNNE27hnqoWLBq+Kpe4iHfeQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"
|
||||
integrity="sha512-gU7kztaQEl7SHJyraPfZLQCNnrKdaQi5ndOyt4L4UPL/FHDd/uB9Je6KDARIqwnNNE27hnqoWLBq+Kpe4iHfeQ=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/js-cookie@3.0.1/dist/js.cookie.min.js"
|
||||
integrity="sha256-0H3Nuz3aug3afVbUlsu12Puxva3CP4EhJtPExqs54Vg=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/luxon@3.0.3/build/global/luxon.min.js" integrity="sha256-RH4TKnKcKyde0s2jc5BW3pXZl/5annY3fcZI9VrV5WQ=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/luxon@3.0.3/build/global/luxon.min.js"
|
||||
integrity="sha256-RH4TKnKcKyde0s2jc5BW3pXZl/5annY3fcZI9VrV5WQ=" crossorigin="anonymous"></script>
|
||||
<script src="static/scripts.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -22,7 +22,7 @@ function revisionClicked(namespace, name, self) {
|
||||
|
||||
const tab = getHashParam("tab")
|
||||
if (!tab) {
|
||||
$("#nav-tab [data-tab=manifests]").click()
|
||||
$("#nav-tab [data-tab=resources]").click()
|
||||
} else {
|
||||
$("#nav-tab [data-tab=" + tab + "]").click()
|
||||
}
|
||||
@@ -41,11 +41,15 @@ $("#nav-tab [data-tab]").click(function () {
|
||||
const flag = getHashParam("udv") === "true";
|
||||
$("#userDefinedVals").prop("checked", flag)
|
||||
|
||||
const mode = getHashParam("mode")
|
||||
if (!mode) {
|
||||
$("#modePanel [data-mode=diff-prev]").trigger('click')
|
||||
if (self.data("tab") === "resources") {
|
||||
showResources(getHashParam("namespace"), getHashParam("chart"), getHashParam("revision"))
|
||||
} else {
|
||||
$("#modePanel [data-mode=" + mode + "]").trigger('click')
|
||||
const mode = getHashParam("mode")
|
||||
if (!mode) {
|
||||
$("#modePanel [data-mode=diff-prev]").trigger('click')
|
||||
} else {
|
||||
$("#modePanel [data-mode=" + mode + "]").trigger('click')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -128,7 +132,7 @@ function loadChartHistory(namespace, name) {
|
||||
$("#sectionDetails h1 span.name").text(name)
|
||||
revRow.empty().append("<div><i class='fa fa-spinner fa-spin fa-2x'></i></div>")
|
||||
$.getJSON("/api/helm/charts/history?chart=" + name + "&namespace=" + namespace).fail(function () {
|
||||
reportError("Failed to get list of clusters")
|
||||
reportError("Failed to get chart details")
|
||||
}).done(function (data) {
|
||||
revRow.empty()
|
||||
for (let x = 0; x < data.length; x++) {
|
||||
@@ -203,7 +207,7 @@ function loadChartsList() {
|
||||
$("#sectionList").show()
|
||||
chartsCards.empty().append("<div><i class='fa fa-spinner fa-spin fa-2x'></i> Loading...</div>")
|
||||
$.getJSON("/api/helm/charts").fail(function () {
|
||||
reportError("Failed to get list of clusters")
|
||||
reportError("Failed to get list of charts")
|
||||
}).done(function (data) {
|
||||
chartsCards.empty()
|
||||
data.forEach(function (elm) {
|
||||
@@ -218,7 +222,7 @@ function loadChartsList() {
|
||||
header.find(".badge").addClass("bg-light text-dark")
|
||||
}
|
||||
|
||||
header.append($('<h5 class="card-title"></h5>').text(elm.name))
|
||||
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>")
|
||||
@@ -304,4 +308,42 @@ function getAge(obj1, obj2) {
|
||||
}
|
||||
}
|
||||
return "n/a"
|
||||
}
|
||||
|
||||
function showResources(namespace, chart, revision) {
|
||||
$("#nav-resources").empty().append("<i class='fa fa-spin fa-spinner fa-2x'></i>");
|
||||
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) {
|
||||
$("#nav-resources").empty();
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const res = data[i]
|
||||
const resBlock = $(`
|
||||
<div class="input-group row">
|
||||
<span class="input-group-text col-sm-2"><em class="text-muted small">` + res.kind + `</em></span>
|
||||
<span class="input-group-text col-sm-6">` + res.metadata.name + `</span>
|
||||
<span class="form-control col-sm-4"><i class="fa fa-spinner fa-spin"></i> <span class="text-muted small">Getting status...</span></span>
|
||||
</div>`)
|
||||
$("#nav-resources").append(resBlock)
|
||||
let ns = res.metadata.namespace ? res.metadata.namespace : namespace
|
||||
$.getJSON("/api/kube/resources/" + res.kind.toLowerCase() + "?name=" + res.metadata.name + "&namespace=" + ns).fail(function () {
|
||||
//reportError("Failed to get list of resources")
|
||||
}).done(function (data) {
|
||||
const badge = $("<span class='badge me-2'></span>").text(data.status.phase);
|
||||
if (["Available", "Active", "Established"].includes(data.status.phase)) {
|
||||
badge.addClass("bg-success")
|
||||
} else if (["Exists"].includes(data.status.phase)) {
|
||||
badge.addClass("bg-success bg-opacity-50")
|
||||
} else {
|
||||
badge.addClass("bg-danger")
|
||||
}
|
||||
|
||||
resBlock.find(".form-control").empty().append(badge).append("<span class='text-muted small'>" + (data.status.message ? data.status.message : '') + "</span>")
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user