mirror of
https://github.com/komodorio/helm-dashboard.git
synced 2026-03-24 11:48: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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user