mirror of
https://github.com/komodorio/helm-dashboard.git
synced 2026-03-24 11:48:04 +00:00
Split data layer into separate files
This commit is contained in:
@@ -5,8 +5,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -17,7 +15,6 @@ import (
|
|||||||
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"helm.sh/helm/v3/pkg/chart"
|
|
||||||
"helm.sh/helm/v3/pkg/release"
|
"helm.sh/helm/v3/pkg/release"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||||
)
|
)
|
||||||
@@ -64,20 +61,6 @@ func (d *DataLayer) runCommandHelm(cmd ...string) (string, error) {
|
|||||||
return d.runCommand(cmd...)
|
return d.runCommand(cmd...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataLayer) runCommandKubectl(cmd ...string) (string, error) {
|
|
||||||
if d.Kubectl == "" {
|
|
||||||
d.Kubectl = "kubectl"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd = append([]string{d.Kubectl}, cmd...)
|
|
||||||
|
|
||||||
if d.KubeContext != "" {
|
|
||||||
cmd = append(cmd, "--context", d.KubeContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
return d.runCommand(cmd...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) forceNamespace(s *string) {
|
func (d *DataLayer) forceNamespace(s *string) {
|
||||||
if d.Namespace != "" {
|
if d.Namespace != "" {
|
||||||
*s = d.Namespace
|
*s = d.Namespace
|
||||||
@@ -102,46 +85,14 @@ func (d *DataLayer) CheckConnectivity() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type KubeContext struct {
|
func (d *DataLayer) GetStatus() *StatusInfo {
|
||||||
IsCurrent bool
|
sum := float64(d.Cache.HitCount + d.Cache.MissCount)
|
||||||
Name string
|
if sum > 0 {
|
||||||
Cluster string
|
d.StatusInfo.CacheHitRatio = float64(d.Cache.HitCount) / sum
|
||||||
AuthInfo string
|
} else {
|
||||||
Namespace string
|
d.StatusInfo.CacheHitRatio = 0
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) ListContexts() (res []KubeContext, err error) {
|
|
||||||
out, err := d.runCommandKubectl("config", "get-contexts")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
return d.StatusInfo
|
||||||
// kubectl has no JSON output for it, we'll have to do custom text parsing
|
|
||||||
lines := strings.Split(out, "\n")
|
|
||||||
|
|
||||||
// find field positions
|
|
||||||
fields := regexp.MustCompile(`(\w+\s+)`).FindAllString(lines[0], -1)
|
|
||||||
cur := len(fields[0])
|
|
||||||
name := cur + len(fields[1])
|
|
||||||
cluster := name + len(fields[2])
|
|
||||||
auth := cluster + len(fields[3])
|
|
||||||
|
|
||||||
// read items
|
|
||||||
for _, line := range lines[1:] {
|
|
||||||
if strings.TrimSpace(line) == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
res = append(res, KubeContext{
|
|
||||||
IsCurrent: strings.TrimSpace(line[0:cur]) == "*",
|
|
||||||
Name: strings.TrimSpace(line[cur:name]),
|
|
||||||
Cluster: strings.TrimSpace(line[name:cluster]),
|
|
||||||
AuthInfo: strings.TrimSpace(line[cluster:auth]),
|
|
||||||
Namespace: strings.TrimSpace(line[auth:]),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataLayer) ListInstalled() (res []ReleaseElement, err error) {
|
func (d *DataLayer) ListInstalled() (res []ReleaseElement, err error) {
|
||||||
@@ -197,70 +148,6 @@ func (d *DataLayer) ReleaseHistory(namespace string, releaseName string) (res []
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataLayer) ChartRepoVersions(chartName string) (res []*RepoChartElement, err error) {
|
|
||||||
search := "/" + chartName + "\v"
|
|
||||||
if strings.Contains(chartName, "/") {
|
|
||||||
search = "\v" + chartName + "\v"
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := []string{"search", "repo", "--regexp", search, "--versions", "--output", "json"}
|
|
||||||
out, err := d.Cache.String(cacheTagRepoVers(chartName), []string{CacheKeyAllRepos}, func() (string, error) {
|
|
||||||
return d.runCommandHelm(cmd...)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(out), &res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) ChartRepoCharts(repoName string) (res []*RepoChartElement, err error) {
|
|
||||||
cmd := []string{"search", "repo", "--regexp", "\v" + repoName + "/", "--output", "json"}
|
|
||||||
out, err := d.Cache.String(cacheTagRepoCharts(repoName), []string{CacheKeyAllRepos}, func() (string, error) {
|
|
||||||
return d.runCommandHelm(cmd...)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(out), &res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ins, err := d.ListInstalled()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
enrichRepoChartsWithInstalled(res, ins)
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func enrichRepoChartsWithInstalled(charts []*RepoChartElement, installed []ReleaseElement) {
|
|
||||||
for _, rchart := range charts {
|
|
||||||
for _, rel := range installed {
|
|
||||||
c, _, err := utils.ChartAndVersion(rel.Chart)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("Failed to parse chart: %s", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pieces := strings.Split(rchart.Name, "/")
|
|
||||||
if pieces[1] == c {
|
|
||||||
// TODO: there can be more than one
|
|
||||||
rchart.InstalledNamespace = rel.Namespace
|
|
||||||
rchart.InstalledName = rel.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SectionFn = func(string, string, int, bool) (string, error) // TODO: rework it into struct-based argument?
|
type SectionFn = func(string, string, int, bool) (string, error) // TODO: rework it into struct-based argument?
|
||||||
|
|
||||||
func (d *DataLayer) RevisionManifests(namespace string, chartName string, revision int, _ bool) (res string, err error) {
|
func (d *DataLayer) RevisionManifests(namespace string, chartName string, revision int, _ bool) (res string, err error) {
|
||||||
@@ -328,63 +215,6 @@ func (d *DataLayer) RevisionValues(namespace string, chartName string, revision
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataLayer) GetResource(namespace string, def *v1.Carp) (*v1.Carp, 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 &v1.Carp{
|
|
||||||
Status: v1.CarpStatus{
|
|
||||||
Phase: "NotFound",
|
|
||||||
Message: err.Error(),
|
|
||||||
Reason: "not found",
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var res v1.Carp
|
|
||||||
err = json.Unmarshal([]byte(out), &res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(res.Status.Conditions, func(i, j int) bool {
|
|
||||||
// some condition types always bubble up
|
|
||||||
if res.Status.Conditions[i].Type == "Available" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.Status.Conditions[j].Type == "Available" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
t1 := res.Status.Conditions[i].LastTransitionTime
|
|
||||||
t2 := res.Status.Conditions[j].LastTransitionTime
|
|
||||||
return t1.Time.Before(t2.Time)
|
|
||||||
})
|
|
||||||
|
|
||||||
return &res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) GetResourceYAML(namespace string, def *v1.Carp) (string, error) {
|
|
||||||
out, err := d.runCommandKubectl("get", strings.ToLower(def.Kind), def.Name, "--namespace", namespace, "--output", "yaml")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) DescribeResource(namespace string, kind string, name string) (string, error) {
|
|
||||||
out, err := d.runCommandKubectl("describe", strings.ToLower(kind), name, "--namespace", namespace)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) ReleaseUninstall(namespace string, name string) error {
|
func (d *DataLayer) ReleaseUninstall(namespace string, name string) error {
|
||||||
d.Cache.Invalidate(CacheKeyRelList, cacheTagRelease(namespace, name))
|
d.Cache.Invalidate(CacheKeyRelList, cacheTagRelease(namespace, name))
|
||||||
|
|
||||||
@@ -399,18 +229,6 @@ func (d *DataLayer) Rollback(namespace string, name string, rev int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataLayer) ChartRepoUpdate(name string) error {
|
|
||||||
d.Cache.Invalidate(cacheTagRepoName(name), CacheKeyAllRepos)
|
|
||||||
|
|
||||||
cmd := []string{"repo", "update"}
|
|
||||||
if name != "" {
|
|
||||||
cmd = append(cmd, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := d.runCommandHelm(cmd...)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) ChartInstall(namespace string, name string, repoChart string, version string, justTemplate bool, values string, reuseVals bool) (string, error) {
|
func (d *DataLayer) ChartInstall(namespace string, name string, repoChart string, version string, justTemplate bool, values string, reuseVals bool) (string, error) {
|
||||||
if values == "" && reuseVals {
|
if values == "" && reuseVals {
|
||||||
oldVals, err := d.RevisionValues(namespace, name, 0, true)
|
oldVals, err := d.RevisionValues(namespace, name, 0, true)
|
||||||
@@ -452,102 +270,6 @@ func (d *DataLayer) ChartInstall(namespace string, name string, repoChart string
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShowValues get values from repo chart, not from installed release
|
|
||||||
func (d *DataLayer) ShowValues(chart string, ver string) (string, error) {
|
|
||||||
return d.Cache.String(CacheKeyRepoChartValues+"\v"+chart+"\v"+ver, nil, func() (string, error) {
|
|
||||||
return d.runCommandHelm("show", "values", chart, "--version", ver)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) ShowChart(chartName string) ([]*chart.Metadata, error) { // TODO: add version parameter to method
|
|
||||||
out, err := d.Cache.String(CacheKeyShowChart+"\v"+chartName, []string{"chart\v" + chartName}, func() (string, error) {
|
|
||||||
return d.runCommandHelm("show", "chart", chartName)
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
deccoder := yaml.NewDecoder(bytes.NewReader([]byte(out)))
|
|
||||||
res := make([]*chart.Metadata, 0)
|
|
||||||
var tmp interface{}
|
|
||||||
|
|
||||||
for deccoder.Decode(&tmp) == nil {
|
|
||||||
jsoned, err := json.Marshal(tmp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var resjson chart.Metadata
|
|
||||||
err = json.Unmarshal(jsoned, &resjson)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res = append(res, &resjson)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) ChartRepoList() (res []RepositoryElement, err error) {
|
|
||||||
out, err := d.Cache.String(CacheKeyAllRepos, nil, func() (string, error) {
|
|
||||||
return d.runCommandHelm("repo", "list", "--output", "json")
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(out), &res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) ChartRepoAdd(name string, url string) (string, error) {
|
|
||||||
d.Cache.Invalidate(CacheKeyAllRepos)
|
|
||||||
out, err := d.runCommandHelm("repo", "add", "--force-update", name, url)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) ChartRepoDelete(name string) (string, error) {
|
|
||||||
d.Cache.Invalidate(CacheKeyAllRepos)
|
|
||||||
out, err := d.runCommandHelm("repo", "remove", name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) GetNameSpaces() (res *NamespaceElement, err error) {
|
|
||||||
out, err := d.runCommandKubectl("get", "namespaces", "-o", "json")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(out), &res)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DataLayer) GetStatus() *StatusInfo {
|
|
||||||
sum := float64(d.Cache.HitCount + d.Cache.MissCount)
|
|
||||||
if sum > 0 {
|
|
||||||
d.StatusInfo.CacheHitRatio = float64(d.Cache.HitCount) / sum
|
|
||||||
} else {
|
|
||||||
d.StatusInfo.CacheHitRatio = 0
|
|
||||||
}
|
|
||||||
return d.StatusInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func RevisionDiff(functor SectionFn, ext string, namespace string, name string, revision1 int, revision2 int, flag bool) (string, error) {
|
func RevisionDiff(functor SectionFn, ext string, namespace string, name string, revision1 int, revision2 int, flag bool) (string, error) {
|
||||||
if revision1 == 0 || revision2 == 0 {
|
if revision1 == 0 || revision2 == 0 {
|
||||||
log.Debugf("One of revisions is zero: %d %d", revision1, revision2)
|
log.Debugf("One of revisions is zero: %d %d", revision1, revision2)
|
||||||
@@ -575,11 +297,3 @@ func GetDiff(text1 string, text2 string, name1 string, name2 string) string {
|
|||||||
log.Debugf("The diff is: %s", diff)
|
log.Debugf("The diff is: %s", diff)
|
||||||
return diff
|
return diff
|
||||||
}
|
}
|
||||||
|
|
||||||
type NamespaceElement struct {
|
|
||||||
Items []struct {
|
|
||||||
Metadata struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
} `json:"metadata"`
|
|
||||||
} `json:"items"`
|
|
||||||
}
|
|
||||||
|
|||||||
144
pkg/dashboard/subproc/kubectl.go
Normal file
144
pkg/dashboard/subproc/kubectl.go
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package subproc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/testapigroup/v1"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *DataLayer) runCommandKubectl(cmd ...string) (string, error) {
|
||||||
|
if d.Kubectl == "" {
|
||||||
|
d.Kubectl = "kubectl"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = append([]string{d.Kubectl}, cmd...)
|
||||||
|
|
||||||
|
if d.KubeContext != "" {
|
||||||
|
cmd = append(cmd, "--context", d.KubeContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.runCommand(cmd...)
|
||||||
|
}
|
||||||
|
|
||||||
|
type KubeContext struct {
|
||||||
|
IsCurrent bool
|
||||||
|
Name string
|
||||||
|
Cluster string
|
||||||
|
AuthInfo string
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) ListContexts() (res []KubeContext, err error) {
|
||||||
|
out, err := d.runCommandKubectl("config", "get-contexts")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// kubectl has no JSON output for it, we'll have to do custom text parsing
|
||||||
|
lines := strings.Split(out, "\n")
|
||||||
|
|
||||||
|
// find field positions
|
||||||
|
fields := regexp.MustCompile(`(\w+\s+)`).FindAllString(lines[0], -1)
|
||||||
|
cur := len(fields[0])
|
||||||
|
name := cur + len(fields[1])
|
||||||
|
cluster := name + len(fields[2])
|
||||||
|
auth := cluster + len(fields[3])
|
||||||
|
|
||||||
|
// read items
|
||||||
|
for _, line := range lines[1:] {
|
||||||
|
if strings.TrimSpace(line) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, KubeContext{
|
||||||
|
IsCurrent: strings.TrimSpace(line[0:cur]) == "*",
|
||||||
|
Name: strings.TrimSpace(line[cur:name]),
|
||||||
|
Cluster: strings.TrimSpace(line[name:cluster]),
|
||||||
|
AuthInfo: strings.TrimSpace(line[cluster:auth]),
|
||||||
|
Namespace: strings.TrimSpace(line[auth:]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamespaceElement struct {
|
||||||
|
Items []struct {
|
||||||
|
Metadata struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"metadata"`
|
||||||
|
} `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) GetNameSpaces() (res *NamespaceElement, err error) {
|
||||||
|
out, err := d.runCommandKubectl("get", "namespaces", "-o", "json")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(out), &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) GetResource(namespace string, def *v1.Carp) (*v1.Carp, 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 &v1.Carp{
|
||||||
|
Status: v1.CarpStatus{
|
||||||
|
Phase: "NotFound",
|
||||||
|
Message: err.Error(),
|
||||||
|
Reason: "not found",
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var res v1.Carp
|
||||||
|
err = json.Unmarshal([]byte(out), &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(res.Status.Conditions, func(i, j int) bool {
|
||||||
|
// some condition types always bubble up
|
||||||
|
if res.Status.Conditions[i].Type == "Available" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Status.Conditions[j].Type == "Available" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
t1 := res.Status.Conditions[i].LastTransitionTime
|
||||||
|
t2 := res.Status.Conditions[j].LastTransitionTime
|
||||||
|
return t1.Time.Before(t2.Time)
|
||||||
|
})
|
||||||
|
|
||||||
|
return &res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) GetResourceYAML(namespace string, def *v1.Carp) (string, error) {
|
||||||
|
out, err := d.runCommandKubectl("get", strings.ToLower(def.Kind), def.Name, "--namespace", namespace, "--output", "yaml")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) DescribeResource(namespace string, kind string, name string) (string, error) {
|
||||||
|
out, err := d.runCommandKubectl("describe", strings.ToLower(kind), name, "--namespace", namespace)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
159
pkg/dashboard/subproc/repos.go
Normal file
159
pkg/dashboard/subproc/repos.go
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
package subproc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/komodorio/helm-dashboard/pkg/dashboard/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"helm.sh/helm/v3/pkg/chart"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *DataLayer) ChartRepoList() (res []RepositoryElement, err error) {
|
||||||
|
out, err := d.Cache.String(CacheKeyAllRepos, nil, func() (string, error) {
|
||||||
|
return d.runCommandHelm("repo", "list", "--output", "json")
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(out), &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) ChartRepoAdd(name string, url string) (string, error) {
|
||||||
|
d.Cache.Invalidate(CacheKeyAllRepos)
|
||||||
|
out, err := d.runCommandHelm("repo", "add", "--force-update", name, url)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) ChartRepoDelete(name string) (string, error) {
|
||||||
|
d.Cache.Invalidate(CacheKeyAllRepos)
|
||||||
|
out, err := d.runCommandHelm("repo", "remove", name)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) ChartRepoUpdate(name string) error {
|
||||||
|
d.Cache.Invalidate(cacheTagRepoName(name), CacheKeyAllRepos)
|
||||||
|
|
||||||
|
cmd := []string{"repo", "update"}
|
||||||
|
if name != "" {
|
||||||
|
cmd = append(cmd, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := d.runCommandHelm(cmd...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) ChartRepoVersions(chartName string) (res []*RepoChartElement, err error) {
|
||||||
|
search := "/" + chartName + "\v"
|
||||||
|
if strings.Contains(chartName, "/") {
|
||||||
|
search = "\v" + chartName + "\v"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := []string{"search", "repo", "--regexp", search, "--versions", "--output", "json"}
|
||||||
|
out, err := d.Cache.String(cacheTagRepoVers(chartName), []string{CacheKeyAllRepos}, func() (string, error) {
|
||||||
|
return d.runCommandHelm(cmd...)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(out), &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) ChartRepoCharts(repoName string) (res []*RepoChartElement, err error) {
|
||||||
|
cmd := []string{"search", "repo", "--regexp", "\v" + repoName + "/", "--output", "json"}
|
||||||
|
out, err := d.Cache.String(cacheTagRepoCharts(repoName), []string{CacheKeyAllRepos}, func() (string, error) {
|
||||||
|
return d.runCommandHelm(cmd...)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(out), &res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ins, err := d.ListInstalled()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
enrichRepoChartsWithInstalled(res, ins)
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func enrichRepoChartsWithInstalled(charts []*RepoChartElement, installed []ReleaseElement) {
|
||||||
|
for _, rchart := range charts {
|
||||||
|
for _, rel := range installed {
|
||||||
|
c, _, err := utils.ChartAndVersion(rel.Chart)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Failed to parse chart: %s", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pieces := strings.Split(rchart.Name, "/")
|
||||||
|
if pieces[1] == c {
|
||||||
|
// TODO: there can be more than one
|
||||||
|
rchart.InstalledNamespace = rel.Namespace
|
||||||
|
rchart.InstalledName = rel.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowValues get values from repo chart, not from installed release
|
||||||
|
func (d *DataLayer) ShowValues(chart string, ver string) (string, error) {
|
||||||
|
return d.Cache.String(CacheKeyRepoChartValues+"\v"+chart+"\v"+ver, nil, func() (string, error) {
|
||||||
|
return d.runCommandHelm("show", "values", chart, "--version", ver)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DataLayer) ShowChart(chartName string) ([]*chart.Metadata, error) { // TODO: add version parameter to method
|
||||||
|
out, err := d.Cache.String(CacheKeyShowChart+"\v"+chartName, []string{"chart\v" + chartName}, func() (string, error) {
|
||||||
|
return d.runCommandHelm("show", "chart", chartName)
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
deccoder := yaml.NewDecoder(bytes.NewReader([]byte(out)))
|
||||||
|
res := make([]*chart.Metadata, 0)
|
||||||
|
var tmp interface{}
|
||||||
|
|
||||||
|
for deccoder.Decode(&tmp) == nil {
|
||||||
|
jsoned, err := json.Marshal(tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resjson chart.Metadata
|
||||||
|
err = json.Unmarshal(jsoned, &resjson)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = append(res, &resjson)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user