diff --git a/cmd/list.go b/cmd/list.go index e16a8695..26488d41 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -32,6 +32,7 @@ func NewListCmd(globalCfg *config.GlobalImpl) *cobra.Command { f := cmd.Flags() f.BoolVar(&listOptions.KeepTempDir, "keep-temp-dir", false, "Keep temporary directory") + f.BoolVar(&listOptions.SkipCharts, "skip-charts", false, "don't prepare charts when listing releases") f.StringVar(&listOptions.Output, "output", "", "output releases list as a json string") return cmd diff --git a/pkg/app/app.go b/pkg/app/app.go index d3067c4a..32ed2b60 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -547,55 +547,31 @@ func (a *App) ListReleases(c ListConfigProvider) error { var releases []*HelmRelease err := a.ForEachState(func(run *Run) (_ bool, errs []error) { - err := run.withPreparedCharts("list", state.ChartPrepareOptions{ - SkipRepos: true, - SkipDeps: true, - Concurrency: 2, - }, func() { - // var releases m - for _, r := range run.state.Releases { - labels := "" - if r.Labels == nil { - r.Labels = map[string]string{} - } - for k, v := range run.state.CommonLabels { - r.Labels[k] = v - } + var stateReleases []*HelmRelease + var err error - var keys []string - for k := range r.Labels { - keys = append(keys, k) - } - sort.Strings(keys) - - for _, k := range keys { - v := r.Labels[k] - labels = fmt.Sprintf("%s,%s:%s", labels, k, v) - } - labels = strings.Trim(labels, ",") - - enabled, err := state.ConditionEnabled(r, run.state.Values()) + if !c.SkipCharts() { + err = run.withPreparedCharts("list", state.ChartPrepareOptions{ + SkipRepos: true, + SkipDeps: true, + Concurrency: 2, + }, func() { + rel, err := a.list(run) if err != nil { panic(err) } - - installed := r.Installed == nil || *r.Installed - releases = append(releases, &HelmRelease{ - Name: r.Name, - Namespace: r.Namespace, - Installed: installed, - Enabled: enabled, - Labels: labels, - Chart: r.Chart, - Version: r.Version, - }) - } - }) + stateReleases = rel + }) + } else { + stateReleases, err = a.list(run) + } if err != nil { errs = append(errs, err) } + releases = append(releases, stateReleases...) + return }, false, SetFilter(true)) @@ -612,6 +588,50 @@ func (a *App) ListReleases(c ListConfigProvider) error { return err } +func (a *App) list(run *Run) ([]*HelmRelease, error) { + var releases []*HelmRelease + + for _, r := range run.state.Releases { + labels := "" + if r.Labels == nil { + r.Labels = map[string]string{} + } + for k, v := range run.state.CommonLabels { + r.Labels[k] = v + } + + var keys []string + for k := range r.Labels { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, k := range keys { + v := r.Labels[k] + labels = fmt.Sprintf("%s,%s:%s", labels, k, v) + } + labels = strings.Trim(labels, ",") + + enabled, err := state.ConditionEnabled(r, run.state.Values()) + if err != nil { + return nil, err + } + + installed := r.Installed == nil || *r.Installed + releases = append(releases, &HelmRelease{ + Name: r.Name, + Namespace: r.Namespace, + Installed: installed, + Enabled: enabled, + Labels: labels, + Chart: r.Chart, + Version: r.Version, + }) + } + + return releases, nil +} + func (a *App) within(dir string, do func() error) error { if dir == "." { return do() diff --git a/pkg/app/app_list_test.go b/pkg/app/app_list_test.go index 0ec63bba..528810ee 100644 --- a/pkg/app/app_list_test.go +++ b/pkg/app/app_list_test.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "io" + "os" "sync" "testing" @@ -17,7 +18,7 @@ import ( "github.com/helmfile/helmfile/pkg/testutil" ) -func TestListWithEnvironment(t *testing.T) { +func testListWithEnvironment(t *testing.T, cfg configImpl) { type testcase struct { environment string ns string @@ -26,7 +27,7 @@ func TestListWithEnvironment(t *testing.T) { expected string } - check := func(t *testing.T, tc testcase) { + check := func(t *testing.T, tc testcase, cfg configImpl) { t.Helper() bs := &bytes.Buffer{} @@ -164,7 +165,7 @@ releases: var listErr error out := testutil.CaptureStdout(func() { - listErr = app.ListReleases(configImpl{}) + listErr = app.ListReleases(cfg) }) var gotErr string @@ -197,14 +198,14 @@ cache my-app true true app:test bitnami/redis database my-app true true bitnami/postgres 11.6.22 global kube-system true true incubator/raw `, - }) + }, cfg) }) t.Run("fail on unknown environment", func(t *testing.T) { check(t, testcase{ environment: "staging", error: `err: no releases found that matches specified selector() and environment(staging), in any helmfile`, - }) + }, cfg) }) t.Run("list releases matching selector and environment", func(t *testing.T) { @@ -215,7 +216,7 @@ global kube-system true true incubator/raw external-secrets default true true app:test,chart:raw,name:external-secrets,namespace:default incubator/raw my-release default true true app:test,chart:raw,name:my-release,namespace:default incubator/raw `, - }) + }, cfg) }) t.Run("filters releases for environment used in one file only", func(t *testing.T) { @@ -225,7 +226,7 @@ my-release default true true app:test,chart:raw,name:my-release, cache my-app true true app:test bitnami/redis 17.0.7 database my-app true true bitnami/postgres 11.6.22 `, - }) + }, cfg) }) t.Run("filters releases for environment used in multiple files", func(t *testing.T) { @@ -243,6 +244,82 @@ test3 true true incubator/raw cache my-app true true app:test bitnami/redis 17.0.7 database my-app true true bitnami/postgres 11.6.22 `, - }) + }, cfg) + }) +} + +func TestListWithEnvironment(t *testing.T) { + t.Run("with skipCharts=false", func(t *testing.T) { + testListWithEnvironment(t, configImpl{skipCharts: false}) + }) + t.Run("with skipCharts=true", func(t *testing.T) { + testListWithEnvironment(t, configImpl{skipCharts: true}) + }) +} + +func testListWithJSONOutput(t *testing.T, cfg configImpl) { + cfg.output = "json" + + files := map[string]string{ + "/path/to/helmfile.d/first.yaml": ` +environments: + default: + values: + - myrelease2: + enabled: false +releases: +- name: myrelease1 + chart: mychart1 + installed: no + labels: + id: myrelease1 +- name: myrelease2 + chart: mychart1 + condition: myrelease2.enabled +`, + "/path/to/helmfile.d/second.yaml": ` +releases: +- name: myrelease3 + chart: mychart1 + installed: yes +- name: myrelease4 + chart: mychart1 + labels: + id: myrelease1 +`, + } + stdout := os.Stdout + defer func() { os.Stdout = stdout }() + + var buffer bytes.Buffer + logger := helmexec.NewLogger(&buffer, "debug") + + app := appWithFs(&App{ + OverrideHelmBinary: DefaultHelmBinary, + fs: ffs.DefaultFileSystem(), + OverrideKubeContext: "default", + Env: "default", + Logger: logger, + Namespace: "testNamespace", + }, files) + + expectNoCallsToHelm(app) + + out := testutil.CaptureStdout(func() { + err := app.ListReleases(cfg) + assert.Nil(t, err) + }) + + expected := `[{"name":"myrelease1","namespace":"","enabled":true,"installed":false,"labels":"id:myrelease1","chart":"mychart1","version":""},{"name":"myrelease2","namespace":"","enabled":false,"installed":true,"labels":"","chart":"mychart1","version":""},{"name":"myrelease3","namespace":"","enabled":true,"installed":true,"labels":"","chart":"mychart1","version":""},{"name":"myrelease4","namespace":"","enabled":true,"installed":true,"labels":"id:myrelease1","chart":"mychart1","version":""}] +` + assert.Equal(t, expected, out) +} + +func TestListWithJSONOutput(t *testing.T) { + t.Run("with skipCharts=false", func(t *testing.T) { + testListWithJSONOutput(t, configImpl{skipCharts: false}) + }) + t.Run("with skipCharts=true", func(t *testing.T) { + testListWithJSONOutput(t, configImpl{skipCharts: true}) }) } diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index 3bd91916..97954f7e 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -2220,6 +2220,7 @@ type configImpl struct { skipNeeds bool includeNeeds bool includeTransitiveNeeds bool + skipCharts bool } func (c configImpl) Selectors() []string { @@ -2294,6 +2295,10 @@ func (c configImpl) Output() string { return c.output } +func (c configImpl) SkipCharts() bool { + return c.skipCharts +} + type applyConfig struct { args string values []string @@ -4622,64 +4627,6 @@ myrelease4 true true id:myrelease1 mychart1 assert.Equal(t, expected, out) } -func TestListWithJsonOutput(t *testing.T) { - files := map[string]string{ - "/path/to/helmfile.d/first.yaml": ` -environments: - default: - values: - - myrelease2: - enabled: false -releases: -- name: myrelease1 - chart: mychart1 - installed: no - labels: - id: myrelease1 -- name: myrelease2 - chart: mychart1 - condition: myrelease2.enabled -`, - "/path/to/helmfile.d/second.yaml": ` -releases: -- name: myrelease3 - chart: mychart1 - installed: yes -- name: myrelease4 - chart: mychart1 - labels: - id: myrelease1 -`, - } - stdout := os.Stdout - defer func() { os.Stdout = stdout }() - - var buffer bytes.Buffer - logger := helmexec.NewLogger(&buffer, "debug") - - app := appWithFs(&App{ - OverrideHelmBinary: DefaultHelmBinary, - fs: ffs.DefaultFileSystem(), - OverrideKubeContext: "default", - Env: "default", - Logger: logger, - Namespace: "testNamespace", - }, files) - - expectNoCallsToHelm(app) - - out := testutil.CaptureStdout(func() { - err := app.ListReleases(configImpl{ - output: "json", - }) - assert.Nil(t, err) - }) - - expected := `[{"name":"myrelease1","namespace":"","enabled":true,"installed":false,"labels":"id:myrelease1","chart":"mychart1","version":""},{"name":"myrelease2","namespace":"","enabled":false,"installed":true,"labels":"","chart":"mychart1","version":""},{"name":"myrelease3","namespace":"","enabled":true,"installed":true,"labels":"","chart":"mychart1","version":""},{"name":"myrelease4","namespace":"","enabled":true,"installed":true,"labels":"id:myrelease1","chart":"mychart1","version":""}] -` - assert.Equal(t, expected, out) -} - func TestSetValuesTemplate(t *testing.T) { files := map[string]string{ "/path/to/helmfile.yaml": ` diff --git a/pkg/app/config.go b/pkg/app/config.go index 30e2053e..a66eff6b 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -236,6 +236,7 @@ type interactive interface { type ListConfigProvider interface { Output() string + SkipCharts() bool } type CacheConfigProvider interface{} diff --git a/pkg/config/list.go b/pkg/config/list.go index 1529a742..b4e76f8a 100644 --- a/pkg/config/list.go +++ b/pkg/config/list.go @@ -6,6 +6,8 @@ type ListOptions struct { Output string // KeepTempDir is the keep temp dir flag KeepTempDir bool + // SkipCharts makes List skip `withPreparedCharts` + SkipCharts bool } // NewListOptions creates a new Apply @@ -36,3 +38,8 @@ func (c *ListImpl) Args() string { func (c *ListImpl) Output() string { return c.ListOptions.Output } + +// SkipCharts returns skipCharts flag +func (c *ListImpl) SkipCharts() bool { + return c.ListOptions.SkipCharts +}