diff --git a/cmd/helm/get.go b/cmd/helm/get.go index 7c4854b59..5ffb7319f 100644 --- a/cmd/helm/get.go +++ b/cmd/helm/get.go @@ -33,6 +33,7 @@ get extended information about the release, including: - The generated manifest file - The notes provided by the chart of the release - The hooks associated with the release +- The dependencies used with the release ` func newGetCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { @@ -48,6 +49,7 @@ func newGetCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { cmd.AddCommand(newGetManifestCmd(cfg, out)) cmd.AddCommand(newGetHooksCmd(cfg, out)) cmd.AddCommand(newGetNotesCmd(cfg, out)) + cmd.AddCommand(newGetDependenciesCmd(cfg, out)) return cmd } diff --git a/cmd/helm/get_dependencies.go b/cmd/helm/get_dependencies.go new file mode 100644 index 000000000..a990348ee --- /dev/null +++ b/cmd/helm/get_dependencies.go @@ -0,0 +1,129 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "io" + + "github.com/gosuri/uitable" + "github.com/spf13/cobra" + + "helm.sh/helm/v3/cmd/helm/require" + "helm.sh/helm/v3/internal/completion" + "helm.sh/helm/v3/pkg/action" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/cli/output" +) + +var getDependenciesHelp = ` +This command list all dependencies for a given release. +` + +type dependencyElement struct { + Name string `json:"name"` + Version string `json:"version,omitempty"` + Repository string `json:"repository"` + Enabled bool `json:"enabled,omitempty"` +} + +type dependencyListWriter struct { + dependencies []dependencyElement +} + +func newGetDependenciesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { + var outfmt output.Format + client := action.NewGet(cfg) + + cmd := &cobra.Command{ + Use: "dependencies RELEASE_NAME", + Short: "download the dependencies for a named release", + Long: getDependenciesHelp, + Args: require.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + res, err := client.Run(args[0]) + + if err != nil { + return err + } + + return outfmt.Write(out, newDependenciesListWriter(res.Chart.Metadata.Dependencies)) + }, + } + + // Function providing dynamic auto-completion + completion.RegisterValidArgsFunc(cmd, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + if len(args) != 0 { + return nil, completion.BashCompDirectiveNoFileComp + } + + return compListReleases(toComplete, cfg) + }) + + f := cmd.Flags() + f.IntVar(&client.Version, "revision", 0, "get the named release with revision") + flag := f.Lookup("revision") + + completion.RegisterFlagCompletionFunc(flag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, completion.BashCompDirective) { + if len(args) == 1 { + return compListRevisions(cfg, args[0]) + } + + return nil, completion.BashCompDirectiveNoFileComp + }) + + bindOutputFlag(cmd, &outfmt) + + return cmd +} + +func newDependenciesListWriter(dependencies []*chart.Dependency) *dependencyListWriter { + // Initialize the array so no results returns an empty array instead of null + elements := make([]dependencyElement, 0, len(dependencies)) + + for _, d := range dependencies { + element := dependencyElement{ + Name: d.Name, + Version: d.Version, + Repository: d.Repository, + Enabled: d.Enabled, + } + + elements = append(elements, element) + } + + return &dependencyListWriter{elements} +} + +func (d dependencyListWriter) WriteTable(out io.Writer) error { + table := uitable.New() + + table.AddRow("NAME", "VERSION", "REPOSITORY", "ENABLED") + + for _, r := range d.dependencies { + table.AddRow(r.Name, r.Version, r.Repository, r.Enabled) + } + + return output.EncodeTable(out, table) +} + +func (d dependencyListWriter) WriteJSON(out io.Writer) error { + return output.EncodeJSON(out, d.dependencies) +} + +func (d dependencyListWriter) WriteYAML(out io.Writer) error { + return output.EncodeYAML(out, d.dependencies) +} diff --git a/cmd/helm/get_dependencies_test.go b/cmd/helm/get_dependencies_test.go new file mode 100644 index 000000000..6804269ce --- /dev/null +++ b/cmd/helm/get_dependencies_test.go @@ -0,0 +1,117 @@ +/* +Copyright The Helm Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "testing" + + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/release" +) + +var StubManifest = `apiVersion: v1 +kind: Secret +metadata: + name: fixture +` + +func TestGetDependencies(t *testing.T) { + chartWithDependencies := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "prims47", + Version: "4.7.7-beta.1", + AppVersion: "1.0", + Dependencies: []*chart.Dependency{ + {Name: "fuego", Version: "7.7.0", Repository: "https://prims47-fuego.com", Enabled: true}, + {Name: "pepito", Version: "4.2.7'", Repository: "https://pepito.com", Enabled: true}, + }, + }, + Templates: []*chart.File{ + {Name: "templates/get-dependencies.tpl", Data: []byte(StubManifest)}, + }, + } + + tests := []cmdTestCase{{ + name: "get dependencies with release (has dependencies)", + cmd: "get dependencies prims47", + golden: "output/get-dependencies.txt", + rels: []*release.Release{ + release.Mock(&release.MockReleaseOptions{ + Name: "prims47", + Chart: chartWithDependencies, + }), + }, + }, { + name: "get dependencies with release (has not dependencies)", + cmd: "get dependencies prims47", + golden: "output/get-dependencies-empty.txt", + rels: []*release.Release{ + release.Mock(&release.MockReleaseOptions{ + Name: "prims47", + }), + }, + }, { + name: "get dependencies without args", + cmd: "get dependencies", + golden: "output/get-dependencies-no-args.txt", + wantError: true, + }, { + name: "get dependencies with release (has dependencies) to json", + cmd: "get dependencies prims47 --output json", + golden: "output/get-dependencies.json", + rels: []*release.Release{ + release.Mock(&release.MockReleaseOptions{ + Name: "prims47", + Chart: chartWithDependencies, + }), + }, + }, { + name: "get dependencies with release (has not dependencies) to json", + cmd: "get dependencies prims47 --output json", + golden: "output/get-dependencies-empty.json", + rels: []*release.Release{ + release.Mock(&release.MockReleaseOptions{ + Name: "prims47", + }), + }, + }, { + name: "get dependencies with release (has dependencies) to yaml", + cmd: "get dependencies prims47 --output yaml", + golden: "output/get-dependencies.yaml", + rels: []*release.Release{ + release.Mock(&release.MockReleaseOptions{ + Name: "prims47", + Chart: chartWithDependencies, + }), + }, + }, { + name: "get dependencies with release (has not dependencies) to yaml", + cmd: "get dependencies prims47 --output yaml", + golden: "output/get-dependencies-empty.yaml", + rels: []*release.Release{ + release.Mock(&release.MockReleaseOptions{ + Name: "prims47", + }), + }, + }} + + runTestCmd(t, tests) +} + +func TestGetDependenciesRevisionCompletion(t *testing.T) { + revisionFlagCompletionTest(t, "get dependencies") +} diff --git a/cmd/helm/testdata/output/get-dependencies-empty.json b/cmd/helm/testdata/output/get-dependencies-empty.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/cmd/helm/testdata/output/get-dependencies-empty.json @@ -0,0 +1 @@ +[] diff --git a/cmd/helm/testdata/output/get-dependencies-empty.txt b/cmd/helm/testdata/output/get-dependencies-empty.txt new file mode 100644 index 000000000..fd93bd395 --- /dev/null +++ b/cmd/helm/testdata/output/get-dependencies-empty.txt @@ -0,0 +1 @@ +NAME VERSION REPOSITORY ENABLED diff --git a/cmd/helm/testdata/output/get-dependencies-empty.yaml b/cmd/helm/testdata/output/get-dependencies-empty.yaml new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/cmd/helm/testdata/output/get-dependencies-empty.yaml @@ -0,0 +1 @@ +[] diff --git a/cmd/helm/testdata/output/get-dependencies-no-args.txt b/cmd/helm/testdata/output/get-dependencies-no-args.txt new file mode 100644 index 000000000..448d6ec12 --- /dev/null +++ b/cmd/helm/testdata/output/get-dependencies-no-args.txt @@ -0,0 +1,3 @@ +Error: "helm get dependencies" requires 1 argument + +Usage: helm get dependencies RELEASE_NAME [flags] diff --git a/cmd/helm/testdata/output/get-dependencies.json b/cmd/helm/testdata/output/get-dependencies.json new file mode 100644 index 000000000..c26bf9f97 --- /dev/null +++ b/cmd/helm/testdata/output/get-dependencies.json @@ -0,0 +1 @@ +[{"name":"fuego","version":"7.7.0","repository":"https://prims47-fuego.com","enabled":true},{"name":"pepito","version":"4.2.7'","repository":"https://pepito.com","enabled":true}] diff --git a/cmd/helm/testdata/output/get-dependencies.txt b/cmd/helm/testdata/output/get-dependencies.txt new file mode 100644 index 000000000..5df64b0e3 --- /dev/null +++ b/cmd/helm/testdata/output/get-dependencies.txt @@ -0,0 +1,3 @@ +NAME VERSION REPOSITORY ENABLED +fuego 7.7.0 https://prims47-fuego.com true +pepito 4.2.7' https://pepito.com true diff --git a/cmd/helm/testdata/output/get-dependencies.yaml b/cmd/helm/testdata/output/get-dependencies.yaml new file mode 100644 index 000000000..e2dc0685e --- /dev/null +++ b/cmd/helm/testdata/output/get-dependencies.yaml @@ -0,0 +1,8 @@ +- enabled: true + name: fuego + repository: https://prims47-fuego.com + version: 7.7.0 +- enabled: true + name: pepito + repository: https://pepito.com + version: 4.2.7'