From 91a7eca06e1378c1510240d9dfe1ddbc4079f982 Mon Sep 17 00:00:00 2001 From: Tariq Ibrahim Date: Wed, 24 Jul 2019 16:22:46 -0700 Subject: [PATCH] parent 7ab62324eb0e4186d4865bfbd809fc319679a297 author Tariq Ibrahim 1564010566 -0700 committer Art Begolli 1565554705 +0100 feat(helm-search): json output for helm search Signed-off-by: Tariq Ibrahim Fix broken link in docs/related.md Looks like this hackernoon link is now broken, probably from when they moved off of Medium. Signed-off-by: Pete Hodgson cleanup: error message typos in sql.go Signed-off-by: ethan feat(): initial json output functionality Signed-off-by: Art Begolli feat(): adding unit test for json flag Signed-off-by: Art Begolli feat(): updating array to json Signed-off-by: Art Begolli feat(): adding error handling feat(): fix failing test chore(): fix linting issues --- cmd/helm/search.go | 48 +++++++++++++++++++++++++++ cmd/helm/search_test.go | 6 ++++ docs/related.md | 2 +- pkg/kube/client.go | 18 +++++++--- pkg/kube/client_test.go | 2 +- pkg/lint/rules/chartfile.go | 4 +-- pkg/storage/driver/sql.go | 2 +- pkg/tiller/environment/environment.go | 1 + 8 files changed, 73 insertions(+), 10 deletions(-) diff --git a/cmd/helm/search.go b/cmd/helm/search.go index 99ffafbd3..5deab0a7c 100644 --- a/cmd/helm/search.go +++ b/cmd/helm/search.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "encoding/json" "fmt" "io" "strings" @@ -48,6 +49,18 @@ type searchCmd struct { regexp bool version string colWidth uint + output string +} + +type resultEntry struct { + Name string `json:"name"` + ChartVersion string `json:"chartVersion"` + AppVersion string `json:"appVersion"` + Description string `json:"description"` +} + +type results struct { + SearchResults []resultEntry `json:"searchResults"` } func newSearchCmd(out io.Writer) *cobra.Command { @@ -68,6 +81,7 @@ func newSearchCmd(out io.Writer) *cobra.Command { f.BoolVarP(&sc.versions, "versions", "l", false, "Show the long listing, with each version of each chart on its own line") f.StringVarP(&sc.version, "version", "v", "", "Search using semantic versioning constraints") f.UintVar(&sc.colWidth, "col-width", 60, "Specifies the max column width of output") + f.StringVarP(&sc.output, "output", "o", "", "Show the output in specified format") return cmd } @@ -95,6 +109,16 @@ func (s *searchCmd) run(args []string) error { return err } + if s.output == "json" { + formattedResults, err := s.formatSearchResultsJSON(data, s.colWidth) + if err != nil { + return err + } + + fmt.Fprintln(s.out, formattedResults) + return nil + } + fmt.Fprintln(s.out, s.formatSearchResults(data, s.colWidth)) return nil @@ -141,6 +165,30 @@ func (s *searchCmd) formatSearchResults(res []*search.Result, colWidth uint) str return table.String() } +func (s *searchCmd) formatSearchResultsJSON(res []*search.Result, colWidth uint) (string, error) { + var resultJSON []resultEntry + + if len(res) == 0 { + return "No results found", nil + } + + for _, r := range res { + resultRow := resultEntry{ + Name: r.Name, + ChartVersion: r.Chart.Version, + AppVersion: r.Chart.AppVersion, + Description: r.Chart.Description, + } + resultJSON = append(resultJSON, resultRow) + } + jsonSearchResults, err := json.MarshalIndent(results{SearchResults: resultJSON}, "", " ") + if err != nil { + return "", err + } + + return string(jsonSearchResults), nil +} + func (s *searchCmd) buildIndex() (*search.Index, error) { // Load the repositories.yaml rf, err := repo.LoadRepositoriesFile(s.helmhome.RepositoryFile()) diff --git a/cmd/helm/search_test.go b/cmd/helm/search_test.go index 233f94572..4617ea151 100644 --- a/cmd/helm/search_test.go +++ b/cmd/helm/search_test.go @@ -84,6 +84,12 @@ func TestSearchCmd(t *testing.T) { flags: []string{"--regexp"}, err: true, }, + { + name: "search for 'alpine', expect two matches in json", + args: []string{"alpine"}, + flags: []string{"--output", "json"}, + expected: "{\n \"searchResults\": \\[\n {\n \"name\": \"testing/alpine\",\n \"chartVersion\": \"0.2.0\",\n \"appVersion\": \"2.3.4\",\n \"description\": \"Deploy a basic Alpine Linux pod\"\n }\n \\]\n}", + }, } cleanup := resetEnv() diff --git a/docs/related.md b/docs/related.md index 06527d75d..63919790b 100644 --- a/docs/related.md +++ b/docs/related.md @@ -14,7 +14,7 @@ or [pull request](https://github.com/helm/helm/pulls). - [GitLab, Consumer Driven Contracts, Helm and Kubernetes](https://medium.com/@enxebre/gitlab-consumer-driven-contracts-helm-and-kubernetes-b7235a60a1cb#.xwp1y4tgi) - [Honestbee's Helm Chart Conventions](https://gist.github.com/so0k/f927a4b60003cedd101a0911757c605a) - [Releasing backward-incompatible changes: Kubernetes, Jenkins, Prometheus Operator, Helm and Traefik](https://medium.com/@enxebre/releasing-backward-incompatible-changes-kubernetes-jenkins-plugin-prometheus-operator-helm-self-6263ca61a1b1#.e0c7elxhq) -- [The Missing CI/CD Kubernetes Component: Helm package manager](https://hackernoon.com/the-missing-ci-cd-kubernetes-component-helm-package-manager-1fe002aac680#.691sk2zhu) +- [The Missing CI/CD Kubernetes Component: Helm package manager](https://medium.com/@gajus/the-missing-ci-cd-kubernetes-component-helm-package-manager-1fe002aac680) - [Using Helm to Deploy to Kubernetes](https://daemonza.github.io/2017/02/20/using-helm-to-deploy-to-kubernetes/) - [Writing a Helm Chart](https://www.influxdata.com/packaged-kubernetes-deployments-writing-helm-chart/) - [A basic walk through Kubernetes Helm](https://github.com/muffin87/helm-tutorial) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index fcaa28760..8fbfba1fd 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -314,7 +314,14 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { return buf.String(), nil } -// Deprecated; use UpdateWithOptions instead +// Update reads the current configuration and a target configuration from io.reader +// and creates resources that don't already exist, updates resources that have been modified +// in the target configuration and deletes resources from the current configuration that are +// not present in the target configuration. +// +// Namespace will set the namespaces. +// +// Deprecated: use UpdateWithOptions instead. func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { return c.UpdateWithOptions(namespace, originalReader, targetReader, UpdateOptions{ Force: force, @@ -334,12 +341,13 @@ type UpdateOptions struct { CleanupOnFail bool } -// UpdateWithOptions reads in the current configuration and a target configuration from io.reader -// and creates resources that don't already exists, updates resources that have been modified +// UpdateWithOptions reads the current configuration and a target configuration from io.reader +// and creates resources that don't already exist, updates resources that have been modified // in the target configuration and deletes resources from the current configuration that are // not present in the target configuration. // -// Namespace will set the namespaces. +// Namespace will set the namespaces. UpdateOptions provides additional parameters to control +// update behavior. func (c *Client) UpdateWithOptions(namespace string, originalReader, targetReader io.Reader, opts UpdateOptions) error { original, err := c.BuildUnstructured(namespace, originalReader) if err != nil { @@ -552,7 +560,7 @@ func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int return perform(infos, c.watchTimeout(time.Duration(timeout)*time.Second)) } -// WatchUntilCRDEstablished polls the given CRD until it reaches the established +// WaitUntilCRDEstablished polls the given CRD until it reaches the established // state. A CRD needs to reach the established state before CRs can be created. // // If a naming conflict condition is found, this function will return an error. diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index 6faea02d0..d33b4b9d9 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -558,7 +558,7 @@ func TestWaitUntilCRDEstablished(t *testing.T) { } else { crd = crdWithConditions } - requestCount += 1 + requestCount++ return newResponse(200, &crd) }), } diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go index 8ef33d0c5..8f6c16d94 100644 --- a/pkg/lint/rules/chartfile.go +++ b/pkg/lint/rules/chartfile.go @@ -51,7 +51,7 @@ func Chartfile(linter *support.Linter) { linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartNameDirMatch(linter.ChartDir, chartFile)) // Chart metadata - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartApiVersion(chartFile)) + linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartVersion(chartFile)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartEngine(chartFile)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartMaintainer(chartFile)) @@ -97,7 +97,7 @@ func validateChartNameDirMatch(chartDir string, cf *chart.Metadata) error { return nil } -func validateChartApiVersion(cf *chart.Metadata) error { +func validateChartAPIVersion(cf *chart.Metadata) error { if cf.ApiVersion == "" { return errors.New("apiVersion is required") } diff --git a/pkg/storage/driver/sql.go b/pkg/storage/driver/sql.go index 46bcccc32..be2962da4 100644 --- a/pkg/storage/driver/sql.go +++ b/pkg/storage/driver/sql.go @@ -199,7 +199,7 @@ func (s *SQL) Query(labels map[string]string) ([]*rspb.Release, error) { sqlFilter[dbField] = val } else { s.Log("unknown label %s", key) - return nil, fmt.Errorf("unknow label %s", key) + return nil, fmt.Errorf("unknown label %s", key) } } sort.Strings(sqlFilterKeys) diff --git a/pkg/tiller/environment/environment.go b/pkg/tiller/environment/environment.go index d84ad55db..83ec5e647 100644 --- a/pkg/tiller/environment/environment.go +++ b/pkg/tiller/environment/environment.go @@ -255,6 +255,7 @@ func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace string, reade return v1.PodUnknown, err } +// WaitUntilCRDEstablished implements KubeClient WaitUntilCRDEstablished. func (p *PrintingKubeClient) WaitUntilCRDEstablished(reader io.Reader, timeout time.Duration) error { _, err := io.Copy(p.Out, reader) return err