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:
Akash Shrivastava
2024-01-16 19:12:43 +00:00
committed by GitHub
parent e542f4fd0a
commit 361e12e691
11 changed files with 94 additions and 26 deletions
@@ -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
View File
@@ -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 {
+3 -1
View File
@@ -1,7 +1,9 @@
---
apiVersion: troubleshoot.sh/v1beta2
kind: SupportBundle
metadata:
name: default
spec:
collectors:
- helm: {}
- helm:
collectValues: true
+4
View File
@@ -0,0 +1,4 @@
---
extraEnvVars:
- name: TEST_ENV_VAR
value: "test-value"