mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-08-27 00:37:20 +00:00
feat: [ISSUE-1401]: Added helm get values option in Helm collector (#1402)
* feat: [ISSUE-1401]: Added helm get values option in Helm collector Signed-off-by: Akash Shrivastava <akash.shrivastava@harness.io> * Made changes in error handling Signed-off-by: Akash Shrivastava <akash.shrivastava@harness.io> * feat: [ISSUE-1401]: Added test cases and fixes Signed-off-by: Akash Shrivastava <akash.shrivastava@harness.io> * fixed test case Signed-off-by: Akash Shrivastava <akash.shrivastava@harness.io> --------- Signed-off-by: Akash Shrivastava <akash.shrivastava@harness.io>
This commit is contained in:
@@ -349,6 +349,8 @@ spec:
|
||||
type: object
|
||||
helm:
|
||||
properties:
|
||||
collectValues:
|
||||
type: boolean
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
|
||||
@@ -1910,6 +1910,8 @@ spec:
|
||||
type: object
|
||||
helm:
|
||||
properties:
|
||||
collectValues:
|
||||
type: boolean
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
|
||||
@@ -1941,6 +1941,8 @@ spec:
|
||||
type: object
|
||||
helm:
|
||||
properties:
|
||||
collectValues:
|
||||
type: boolean
|
||||
collectorName:
|
||||
type: string
|
||||
exclude:
|
||||
|
||||
@@ -257,6 +257,7 @@ type Helm struct {
|
||||
CollectorMeta `json:",inline" yaml:",inline"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
ReleaseName string `json:"releaseName,omitempty" yaml:"releaseName,omitempty"`
|
||||
CollectValues bool `json:"collectValues,omitempty" yaml:"collectValues,omitempty"`
|
||||
}
|
||||
|
||||
type Goldpinger struct {
|
||||
|
||||
+68
-23
@@ -5,12 +5,11 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
|
||||
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
@@ -39,10 +38,11 @@ type ReleaseInfo struct {
|
||||
|
||||
// Helm release version information struct
|
||||
type VersionInfo struct {
|
||||
Revision string `json:"revision"`
|
||||
Date string `json:"date"`
|
||||
Status string `json:"status"`
|
||||
IsPending bool `json:"isPending,omitempty"`
|
||||
Revision string `json:"revision"`
|
||||
Date string `json:"date"`
|
||||
Status string `json:"status"`
|
||||
IsPending bool `json:"isPending,omitempty"`
|
||||
Values map[string]interface{} `json:"values,omitempty"`
|
||||
}
|
||||
|
||||
func (c *CollectHelm) Title() string {
|
||||
@@ -57,9 +57,15 @@ func (c *CollectHelm) Collect(progressChan chan<- interface{}) (CollectorResult,
|
||||
|
||||
output := NewResult()
|
||||
|
||||
releaseInfos, err := helmReleaseHistoryCollector(c.Collector.ReleaseName, c.Collector.Namespace)
|
||||
releaseInfos, err := helmReleaseHistoryCollector(c.Collector.ReleaseName, c.Collector.Namespace, c.Collector.CollectValues)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get Helm release history")
|
||||
errsToMarhsal := []string{}
|
||||
for _, e := range err {
|
||||
errsToMarhsal = append(errsToMarhsal, e.Error())
|
||||
}
|
||||
output.SaveResult(c.BundlePath, "helm/errors.json", marshalErrors(errsToMarhsal))
|
||||
klog.Errorf("error collecting helm release info: %v", err)
|
||||
return output, nil
|
||||
}
|
||||
|
||||
releaseInfoByNamespace := helmReleaseInfoByNamespaces(releaseInfos)
|
||||
@@ -72,22 +78,23 @@ func (c *CollectHelm) Collect(progressChan chan<- interface{}) (CollectorResult,
|
||||
}
|
||||
|
||||
filePath := fmt.Sprintf("helm/%s.json", namespace)
|
||||
|
||||
err := output.SaveResult(c.BundlePath, filePath, bytes.NewBuffer(helmHistoryJson))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if c.Collector.ReleaseName != "" {
|
||||
filePath = fmt.Sprintf("helm/%s/%s.json", namespace, c.Collector.ReleaseName)
|
||||
}
|
||||
|
||||
output.SaveResult(c.BundlePath, filePath, bytes.NewBuffer(helmHistoryJson))
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func helmReleaseHistoryCollector(releaseName string, namespace string) ([]ReleaseInfo, error) {
|
||||
func helmReleaseHistoryCollector(releaseName string, namespace string, collectValues bool) ([]ReleaseInfo, []error) {
|
||||
var results []ReleaseInfo
|
||||
error_list := []error{}
|
||||
|
||||
actionConfig := new(action.Configuration)
|
||||
if err := actionConfig.Init(nil, namespace, "", klog.V(2).Infof); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to initialize Helm action config")
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
// If releaseName is specified, get the history of that release
|
||||
@@ -95,17 +102,18 @@ func helmReleaseHistoryCollector(releaseName string, namespace string) ([]Releas
|
||||
getAction := action.NewGet(actionConfig)
|
||||
r, err := getAction.Run(releaseName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get Helm release %s", releaseName)
|
||||
return nil, []error{err}
|
||||
}
|
||||
versionInfo, err := getVersionInfo(actionConfig, r.Name, r.Namespace, collectValues)
|
||||
results = append(results, ReleaseInfo{
|
||||
ReleaseName: r.Name,
|
||||
Chart: r.Chart.Metadata.Name,
|
||||
ChartVersion: r.Chart.Metadata.Version,
|
||||
AppVersion: r.Chart.Metadata.AppVersion,
|
||||
Namespace: r.Namespace,
|
||||
VersionInfo: getVersionInfo(actionConfig, releaseName),
|
||||
VersionInfo: versionInfo,
|
||||
})
|
||||
return results, nil
|
||||
return results, []error{err}
|
||||
}
|
||||
|
||||
// If releaseName is not specified, get the history of all releases
|
||||
@@ -114,39 +122,64 @@ func helmReleaseHistoryCollector(releaseName string, namespace string) ([]Releas
|
||||
listAction := action.NewList(actionConfig)
|
||||
releases, err := listAction.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to list Helm releases: %v", err)
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
for _, r := range releases {
|
||||
versionInfo, err := getVersionInfo(actionConfig, r.Name, r.Namespace, collectValues)
|
||||
if err != nil {
|
||||
error_list = append(error_list, err)
|
||||
}
|
||||
results = append(results, ReleaseInfo{
|
||||
ReleaseName: r.Name,
|
||||
Chart: r.Chart.Metadata.Name,
|
||||
ChartVersion: r.Chart.Metadata.Version,
|
||||
AppVersion: r.Chart.Metadata.AppVersion,
|
||||
Namespace: r.Namespace,
|
||||
VersionInfo: getVersionInfo(actionConfig, r.Name),
|
||||
VersionInfo: versionInfo,
|
||||
})
|
||||
}
|
||||
|
||||
if len(error_list) > 0 {
|
||||
return nil, error_list
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func getVersionInfo(actionConfig *action.Configuration, releaseName string) []VersionInfo {
|
||||
func getVersionInfo(actionConfig *action.Configuration, releaseName, namespace string, collectValues bool) ([]VersionInfo, error) {
|
||||
|
||||
versionCollect := []VersionInfo{}
|
||||
error_list := []error{}
|
||||
|
||||
history, _ := action.NewHistory(actionConfig).Run(releaseName)
|
||||
history, err := action.NewHistory(actionConfig).Run(releaseName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, release := range history {
|
||||
values := map[string]interface{}{}
|
||||
if collectValues {
|
||||
values, err = getHelmValues(releaseName, namespace, release.Version)
|
||||
if err != nil {
|
||||
error_list = append(error_list, err)
|
||||
}
|
||||
}
|
||||
|
||||
versionCollect = append(versionCollect, VersionInfo{
|
||||
Revision: strconv.Itoa(release.Version),
|
||||
Date: release.Info.LastDeployed.String(),
|
||||
Status: release.Info.Status.String(),
|
||||
IsPending: release.Info.Status.IsPending(),
|
||||
Values: values,
|
||||
})
|
||||
}
|
||||
return versionCollect
|
||||
if len(error_list) > 0 {
|
||||
errs := []string{}
|
||||
for _, e := range error_list {
|
||||
errs = append(errs, e.Error())
|
||||
}
|
||||
return nil, errors.New(strings.Join(errs, "\n"))
|
||||
}
|
||||
return versionCollect, nil
|
||||
}
|
||||
|
||||
func helmReleaseInfoByNamespaces(releaseInfo []ReleaseInfo) map[string][]ReleaseInfo {
|
||||
@@ -158,3 +191,15 @@ func helmReleaseInfoByNamespaces(releaseInfo []ReleaseInfo) map[string][]Release
|
||||
|
||||
return releaseInfoByNamespace
|
||||
}
|
||||
|
||||
func getHelmValues(releaseName, namespace string, revision int) (map[string]interface{}, error) {
|
||||
actionConfig := new(action.Configuration)
|
||||
if err := actionConfig.Init(nil, namespace, "", klog.V(2).Infof); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
getAction := action.NewGetValues(actionConfig)
|
||||
getAction.AllValues = true
|
||||
getAction.Version = revision
|
||||
helmValues, err := getAction.Run(releaseName)
|
||||
return helmValues, err
|
||||
}
|
||||
|
||||
@@ -483,6 +483,9 @@
|
||||
"helm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collectValues": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"collectorName": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -2888,6 +2888,9 @@
|
||||
"helm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collectValues": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"collectorName": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -2934,6 +2934,9 @@
|
||||
"helm": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"collectValues": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"collectorName": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -27,7 +27,7 @@ func Test_HelmCollector(t *testing.T) {
|
||||
Setup(func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
|
||||
cluster := getClusterFromContext(t, ctx, ClusterName)
|
||||
manager := helm.New(cluster.GetKubeconfig())
|
||||
manager.RunInstall(helm.WithName(releaseName), helm.WithNamespace(c.Namespace()), helm.WithChart(filepath.Join(curDir, "testdata/charts/nginx-15.2.0.tgz")), helm.WithWait(), helm.WithTimeout("1m"))
|
||||
manager.RunInstall(helm.WithName(releaseName), helm.WithNamespace(c.Namespace()), helm.WithChart(filepath.Join(curDir, "testdata/charts/nginx-15.2.0.tgz")), helm.WithArgs("-f "+filepath.Join(curDir, "testdata/helm-values.yaml")), helm.WithWait(), helm.WithTimeout("1m"))
|
||||
//ignore error to allow test to speed up, helm collector will catch the pending or deployed helm release status
|
||||
return ctx
|
||||
}).
|
||||
@@ -62,10 +62,11 @@ func Test_HelmCollector(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, 1, len(results))
|
||||
assert.Equal(t, releaseName, results[0].ReleaseName)
|
||||
assert.Equal(t, "nginx", results[0].Chart)
|
||||
assert.Equal(t, map[string]interface{}{"name": "TEST_ENV_VAR", "value": "test-value"}, results[0].VersionInfo[0].Values["extraEnvVars"].([]interface{})[0])
|
||||
assert.Equal(t, "1.25.2-debian-11-r3", results[0].VersionInfo[0].Values["image"].(map[string]interface{})["tag"])
|
||||
return ctx
|
||||
}).
|
||||
Teardown(func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
---
|
||||
apiVersion: troubleshoot.sh/v1beta2
|
||||
kind: SupportBundle
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
collectors:
|
||||
- helm: {}
|
||||
- helm:
|
||||
collectValues: true
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
extraEnvVars:
|
||||
- name: TEST_ENV_VAR
|
||||
value: "test-value"
|
||||
Reference in New Issue
Block a user