From 35d4c93c0b97b9070b9f773454885c072e6b2f11 Mon Sep 17 00:00:00 2001 From: Yonatan Kahana Date: Thu, 7 May 2020 21:40:21 +0300 Subject: [PATCH] feature: Add --output option to `helm dep list` Signed-off-by: Yonatan Kahana --- cmd/helm/dependency.go | 9 ++- pkg/action/dependency.go | 66 +++++++++++++++---- pkg/action/dependency_test.go | 59 ++++++++++++++++- .../testdata/output/compressed-deps-tgz.json | 1 + .../testdata/output/compressed-deps-tgz.yaml | 4 ++ .../testdata/output/compressed-deps.json | 1 + .../testdata/output/compressed-deps.yaml | 4 ++ pkg/action/testdata/output/missing-deps.json | 1 + pkg/action/testdata/output/missing-deps.yaml | 4 ++ .../output/uncompressed-deps-tgz.json | 1 + .../output/uncompressed-deps-tgz.yaml | 4 ++ .../testdata/output/uncompressed-deps.json | 1 + .../testdata/output/uncompressed-deps.yaml | 4 ++ 13 files changed, 143 insertions(+), 16 deletions(-) create mode 100644 pkg/action/testdata/output/compressed-deps-tgz.json create mode 100644 pkg/action/testdata/output/compressed-deps-tgz.yaml create mode 100644 pkg/action/testdata/output/compressed-deps.json create mode 100644 pkg/action/testdata/output/compressed-deps.yaml create mode 100644 pkg/action/testdata/output/missing-deps.json create mode 100644 pkg/action/testdata/output/missing-deps.yaml create mode 100644 pkg/action/testdata/output/uncompressed-deps-tgz.json create mode 100644 pkg/action/testdata/output/uncompressed-deps-tgz.yaml create mode 100644 pkg/action/testdata/output/uncompressed-deps.json create mode 100644 pkg/action/testdata/output/uncompressed-deps.yaml diff --git a/cmd/helm/dependency.go b/cmd/helm/dependency.go index 2cc4c5045..a2872e1b1 100644 --- a/cmd/helm/dependency.go +++ b/cmd/helm/dependency.go @@ -23,6 +23,7 @@ import ( "helm.sh/helm/v3/cmd/helm/require" "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/cli/output" ) const dependencyDesc = ` @@ -99,8 +100,7 @@ func newDependencyCmd(out io.Writer) *cobra.Command { } func newDependencyListCmd(out io.Writer) *cobra.Command { - client := action.NewDependency() - + var outfmt output.Format cmd := &cobra.Command{ Use: "list CHART", Aliases: []string{"ls"}, @@ -112,8 +112,11 @@ func newDependencyListCmd(out io.Writer) *cobra.Command { if len(args) > 0 { chartpath = filepath.Clean(args[0]) } - return client.List(chartpath, out) + return outfmt.Write(out, &action.DependencyListWriter{Chartpath: chartpath}) }, } + + bindOutputFlag(cmd, &outfmt) + return cmd } diff --git a/pkg/action/dependency.go b/pkg/action/dependency.go index 4a4b8ebad..130db2625 100644 --- a/pkg/action/dependency.go +++ b/pkg/action/dependency.go @@ -27,6 +27,7 @@ import ( "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/cli/output" ) // Dependency is the action for building a given chart's dependency tree. @@ -43,25 +44,66 @@ func NewDependency() *Dependency { return &Dependency{} } -// List executes 'helm dependency list'. -func (d *Dependency) List(chartpath string, out io.Writer) error { - c, err := loader.Load(chartpath) +type dependencyListElement struct { + Name string `json:"name"` + Version string `json:"version"` + Repository string `json:"repository"` + Status string `json:"status"` +} + +type DependencyListWriter struct { + Chartpath string +} + +func (w *DependencyListWriter) WriteTable(out io.Writer) error { + c, err := loader.Load(w.Chartpath) if err != nil { return err } if c.Metadata.Dependencies == nil { - fmt.Fprintf(out, "WARNING: no dependencies at %s\n", filepath.Join(chartpath, "charts")) + fmt.Fprintf(out, "WARNING: no dependencies at %s\n", filepath.Join(w.Chartpath, "charts")) return nil } - d.printDependencies(chartpath, out, c) + w.printDependenciesTable(w.Chartpath, out, c) fmt.Fprintln(out) - d.printMissing(chartpath, out, c.Metadata.Dependencies) + w.printMissing(w.Chartpath, out, c.Metadata.Dependencies) + return nil +} + +func (w *DependencyListWriter) WriteJSON(out io.Writer) error { + return w.encodeByFormat(out, output.JSON) +} + +func (w *DependencyListWriter) WriteYAML(out io.Writer) error { + return w.encodeByFormat(out, output.YAML) +} + +func (w *DependencyListWriter) encodeByFormat(out io.Writer, format output.Format) error { + c, err := loader.Load(w.Chartpath) + if err != nil { + return err + } + + // Initialize the array so no results returns an empty array instead of null + elements := make([]dependencyListElement, 0, len(c.Metadata.Dependencies)) + + for _, d := range c.Metadata.Dependencies { + elements = append(elements, dependencyListElement{Name: d.Name, Repository: d.Repository, Status: w.dependencyStatus(w.Chartpath, d, c), Version: d.Version}) + } + + switch format { + case output.JSON: + return output.EncodeJSON(out, elements) + case output.YAML: + return output.EncodeYAML(out, elements) + } + return nil } -func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, parent *chart.Chart) string { +func (w *DependencyListWriter) dependencyStatus(chartpath string, dep *chart.Dependency, parent *chart.Chart) string { filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*") // If a chart is unpacked, this will check the unpacked chart's `charts/` directory for tarballs. @@ -137,20 +179,20 @@ func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, p return "unpacked" } -// printDependencies prints all of the dependencies in the yaml file. -func (d *Dependency) printDependencies(chartpath string, out io.Writer, c *chart.Chart) { +// printDependenciesTable prints all of the dependencies in the yaml file. +func (w *DependencyListWriter) printDependenciesTable(chartpath string, out io.Writer, c *chart.Chart) { table := uitable.New() table.MaxColWidth = 80 table.AddRow("NAME", "VERSION", "REPOSITORY", "STATUS") for _, row := range c.Metadata.Dependencies { - table.AddRow(row.Name, row.Version, row.Repository, d.dependencyStatus(chartpath, row, c)) + table.AddRow(row.Name, row.Version, row.Repository, w.dependencyStatus(chartpath, row, c)) } fmt.Fprintln(out, table) } // printMissing prints warnings about charts that are present on disk, but are // not in Charts.yaml. -func (d *Dependency) printMissing(chartpath string, out io.Writer, reqs []*chart.Dependency) { +func (w *DependencyListWriter) printMissing(chartpath string, out io.Writer, reqs []*chart.Dependency) { folder := filepath.Join(chartpath, "charts/*") files, err := filepath.Glob(folder) if err != nil { @@ -161,7 +203,7 @@ func (d *Dependency) printMissing(chartpath string, out io.Writer, reqs []*chart for _, f := range files { fi, err := os.Stat(f) if err != nil { - fmt.Fprintf(out, "Warning: %s\n", err) + fmt.Fprintf(out, "WARNING: %s\n", err) } // Skip anything that is not a directory and not a tgz file. if !fi.IsDir() && filepath.Ext(f) != ".tgz" { diff --git a/pkg/action/dependency_test.go b/pkg/action/dependency_test.go index 158acbfb9..558c5f77a 100644 --- a/pkg/action/dependency_test.go +++ b/pkg/action/dependency_test.go @@ -21,36 +21,93 @@ import ( "testing" "helm.sh/helm/v3/internal/test" + "helm.sh/helm/v3/pkg/cli/output" ) func TestList(t *testing.T) { for _, tcase := range []struct { chart string golden string + outfmt output.Format }{ { chart: "testdata/charts/chart-with-compressed-dependencies", golden: "output/compressed-deps.txt", + outfmt: output.Table, }, { chart: "testdata/charts/chart-with-compressed-dependencies-2.1.8.tgz", golden: "output/compressed-deps-tgz.txt", + outfmt: output.Table, }, { chart: "testdata/charts/chart-with-uncompressed-dependencies", golden: "output/uncompressed-deps.txt", + outfmt: output.Table, }, { chart: "testdata/charts/chart-with-uncompressed-dependencies-2.1.8.tgz", golden: "output/uncompressed-deps-tgz.txt", + outfmt: output.Table, }, { chart: "testdata/charts/chart-missing-deps", golden: "output/missing-deps.txt", + outfmt: output.Table, + }, + { + chart: "testdata/charts/chart-with-compressed-dependencies", + golden: "output/compressed-deps.json", + outfmt: output.JSON, + }, + { + chart: "testdata/charts/chart-with-compressed-dependencies-2.1.8.tgz", + golden: "output/compressed-deps-tgz.json", + outfmt: output.JSON, + }, + { + chart: "testdata/charts/chart-with-uncompressed-dependencies", + golden: "output/uncompressed-deps.json", + outfmt: output.JSON, + }, + { + chart: "testdata/charts/chart-with-uncompressed-dependencies-2.1.8.tgz", + golden: "output/uncompressed-deps-tgz.json", + outfmt: output.JSON, + }, + { + chart: "testdata/charts/chart-missing-deps", + golden: "output/missing-deps.json", + outfmt: output.JSON, + }, + { + chart: "testdata/charts/chart-with-compressed-dependencies", + golden: "output/compressed-deps.yaml", + outfmt: output.YAML, + }, + { + chart: "testdata/charts/chart-with-compressed-dependencies-2.1.8.tgz", + golden: "output/compressed-deps-tgz.yaml", + outfmt: output.YAML, + }, + { + chart: "testdata/charts/chart-with-uncompressed-dependencies", + golden: "output/uncompressed-deps.yaml", + outfmt: output.YAML, + }, + { + chart: "testdata/charts/chart-with-uncompressed-dependencies-2.1.8.tgz", + golden: "output/uncompressed-deps-tgz.yaml", + outfmt: output.YAML, + }, + { + chart: "testdata/charts/chart-missing-deps", + golden: "output/missing-deps.yaml", + outfmt: output.YAML, }, } { buf := bytes.Buffer{} - if err := NewDependency().List(tcase.chart, &buf); err != nil { + if err := tcase.outfmt.Write(&buf, &DependencyListWriter{Chartpath: tcase.chart}); err != nil { t.Fatal(err) } test.AssertGoldenBytes(t, buf.Bytes(), tcase.golden) diff --git a/pkg/action/testdata/output/compressed-deps-tgz.json b/pkg/action/testdata/output/compressed-deps-tgz.json new file mode 100644 index 000000000..a7961ce0b --- /dev/null +++ b/pkg/action/testdata/output/compressed-deps-tgz.json @@ -0,0 +1 @@ +[{"name":"mariadb","version":"4.x.x","repository":"https://kubernetes-charts.storage.googleapis.com/","status":"unpacked"}] diff --git a/pkg/action/testdata/output/compressed-deps-tgz.yaml b/pkg/action/testdata/output/compressed-deps-tgz.yaml new file mode 100644 index 000000000..81e36a277 --- /dev/null +++ b/pkg/action/testdata/output/compressed-deps-tgz.yaml @@ -0,0 +1,4 @@ +- name: mariadb + repository: https://kubernetes-charts.storage.googleapis.com/ + status: unpacked + version: 4.x.x diff --git a/pkg/action/testdata/output/compressed-deps.json b/pkg/action/testdata/output/compressed-deps.json new file mode 100644 index 000000000..4af8c0935 --- /dev/null +++ b/pkg/action/testdata/output/compressed-deps.json @@ -0,0 +1 @@ +[{"name":"mariadb","version":"4.x.x","repository":"https://kubernetes-charts.storage.googleapis.com/","status":"ok"}] diff --git a/pkg/action/testdata/output/compressed-deps.yaml b/pkg/action/testdata/output/compressed-deps.yaml new file mode 100644 index 000000000..52f87ad56 --- /dev/null +++ b/pkg/action/testdata/output/compressed-deps.yaml @@ -0,0 +1,4 @@ +- name: mariadb + repository: https://kubernetes-charts.storage.googleapis.com/ + status: ok + version: 4.x.x diff --git a/pkg/action/testdata/output/missing-deps.json b/pkg/action/testdata/output/missing-deps.json new file mode 100644 index 000000000..9cf98334c --- /dev/null +++ b/pkg/action/testdata/output/missing-deps.json @@ -0,0 +1 @@ +[{"name":"mariadb","version":"4.x.x","repository":"https://kubernetes-charts.storage.googleapis.com/","status":"missing"}] diff --git a/pkg/action/testdata/output/missing-deps.yaml b/pkg/action/testdata/output/missing-deps.yaml new file mode 100644 index 000000000..e7f51336e --- /dev/null +++ b/pkg/action/testdata/output/missing-deps.yaml @@ -0,0 +1,4 @@ +- name: mariadb + repository: https://kubernetes-charts.storage.googleapis.com/ + status: missing + version: 4.x.x diff --git a/pkg/action/testdata/output/uncompressed-deps-tgz.json b/pkg/action/testdata/output/uncompressed-deps-tgz.json new file mode 100644 index 000000000..a7961ce0b --- /dev/null +++ b/pkg/action/testdata/output/uncompressed-deps-tgz.json @@ -0,0 +1 @@ +[{"name":"mariadb","version":"4.x.x","repository":"https://kubernetes-charts.storage.googleapis.com/","status":"unpacked"}] diff --git a/pkg/action/testdata/output/uncompressed-deps-tgz.yaml b/pkg/action/testdata/output/uncompressed-deps-tgz.yaml new file mode 100644 index 000000000..81e36a277 --- /dev/null +++ b/pkg/action/testdata/output/uncompressed-deps-tgz.yaml @@ -0,0 +1,4 @@ +- name: mariadb + repository: https://kubernetes-charts.storage.googleapis.com/ + status: unpacked + version: 4.x.x diff --git a/pkg/action/testdata/output/uncompressed-deps.json b/pkg/action/testdata/output/uncompressed-deps.json new file mode 100644 index 000000000..a7961ce0b --- /dev/null +++ b/pkg/action/testdata/output/uncompressed-deps.json @@ -0,0 +1 @@ +[{"name":"mariadb","version":"4.x.x","repository":"https://kubernetes-charts.storage.googleapis.com/","status":"unpacked"}] diff --git a/pkg/action/testdata/output/uncompressed-deps.yaml b/pkg/action/testdata/output/uncompressed-deps.yaml new file mode 100644 index 000000000..81e36a277 --- /dev/null +++ b/pkg/action/testdata/output/uncompressed-deps.yaml @@ -0,0 +1,4 @@ +- name: mariadb + repository: https://kubernetes-charts.storage.googleapis.com/ + status: unpacked + version: 4.x.x