Unverified Commit 0a488670 authored by Taylor Thomas's avatar Taylor Thomas Committed by GitHub

Merge pull request #5593 from deschmih/master

Flag --output json and yaml added
parents 7a0bbe16 a52d6de9
......@@ -143,6 +143,7 @@ type installCmd struct {
certFile string
keyFile string
caFile string
output string
}
type valueFiles []string
......@@ -226,6 +227,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&inst.depUp, "dep-up", false, "Run helm dependency update before installing the chart")
f.BoolVar(&inst.subNotes, "render-subchart-notes", false, "Render subchart notes along with the parent")
f.StringVar(&inst.description, "description", "", "Specify a description for the release")
f.StringVarP(&inst.output, "output", "o", "table", "Prints the output in the specified format (json|table|yaml)")
// set defaults from environment
settings.InitTLS(f)
......@@ -335,7 +337,10 @@ func (i *installCmd) run() error {
if rel == nil {
return nil
}
if i.output == "table" {
i.printRelease(rel)
}
// If this is a dry run, we can't display status.
if i.dryRun {
......@@ -351,7 +356,15 @@ func (i *installCmd) run() error {
if err != nil {
return prettyError(err)
}
PrintStatus(i.out, status)
output, err := PrintStatusFormated(i.output, status)
if err != nil {
return err
}
fmt.Fprintf(i.out, output)
return nil
}
......
......@@ -191,6 +191,22 @@ func TestInstall(t *testing.T) {
flags: []string{"--name-template", "{{UPPER \"foobar\"}}"},
err: true,
},
// Install, using --output json
{
name: "install using output json",
args: []string{"testdata/testcharts/alpine"},
flags: strings.Split("--name virgil --output json", " "),
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}),
expected: regexp.QuoteMeta(`{"name":"virgil","info":{"status":{"code":1},"first_deployed":{"seconds":242085845},"last_deployed":{"seconds":242085845},"Description":"Release mock"},"namespace":"default"}`),
},
// Install, using --output yaml
{
name: "install using output yaml",
args: []string{"testdata/testcharts/alpine"},
flags: strings.Split("--name virgil --output yaml", " "),
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}),
expected: "info:\n Description: Release mock\n first_deployed:\n seconds: 242085845\n last_deployed:\n seconds: 242085845\n status:\n code: 1\nname: virgil\nnamespace: default\n",
},
}
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {
......
......@@ -17,10 +17,12 @@ limitations under the License.
package main
import (
"errors"
"encoding/json"
"fmt"
"io"
"strings"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
......@@ -31,6 +33,16 @@ import (
type repoListCmd struct {
out io.Writer
home helmpath.Home
output string
}
type repositoryElement struct {
Name string
URL string
}
type repositories struct {
Repositories []*repositoryElement
}
func newRepoListCmd(out io.Writer) *cobra.Command {
......@@ -45,22 +57,70 @@ func newRepoListCmd(out io.Writer) *cobra.Command {
},
}
f := cmd.Flags()
f.StringVarP(&list.output, "output", "o", "table", "Prints the output in the specified format (json|table|yaml)")
return cmd
}
func (a *repoListCmd) run() error {
f, err := repo.LoadRepositoriesFile(a.home.RepositoryFile())
repoFile, err := repo.LoadRepositoriesFile(a.home.RepositoryFile())
if err != nil {
return err
}
output, err := formatRepoListResult(a.output, repoFile)
if err != nil {
return err
}
if len(f.Repositories) == 0 {
return errors.New("no repositories to show")
fmt.Fprintln(a.out, output)
return nil
}
func formatRepoListResult(format string, repoFile *repo.RepoFile) (string, error) {
var output string
var err error
if len(repoFile.Repositories) == 0 {
err = fmt.Errorf("no repositories to show")
return output, err
}
switch format {
case "table":
table := uitable.New()
table.AddRow("NAME", "URL")
for _, re := range f.Repositories {
for _, re := range repoFile.Repositories {
table.AddRow(re.Name, re.URL)
}
fmt.Fprintln(a.out, table)
return nil
output = table.String()
case "json":
output, err = printFormatedRepoFile(format, repoFile, json.Marshal)
case "yaml":
output, err = printFormatedRepoFile(format, repoFile, yaml.Marshal)
}
return output, err
}
func printFormatedRepoFile(format string, repoFile *repo.RepoFile, obj func(v interface{}) ([]byte, error)) (string, error) {
var output string
var err error
var repolist repositories
for _, re := range repoFile.Repositories {
repolist.Repositories = append(repolist.Repositories, &repositoryElement{Name: re.Name, URL: re.URL})
}
o, e := obj(repolist)
if e != nil {
err = fmt.Errorf("Failed to Marshal %s output: %s", strings.ToUpper(format), e)
} else {
output = string(o)
}
return output, err
}
......@@ -17,11 +17,13 @@ limitations under the License.
package main
import (
"encoding/json"
"fmt"
"io"
"strings"
"github.com/Masterminds/semver"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
......@@ -48,6 +50,18 @@ type searchCmd struct {
regexp bool
version string
colWidth uint
output string
}
type chartElement struct {
Name string
Version string
AppVersion string
Description string
}
type searchResult struct {
Charts []*chartElement
}
func newSearchCmd(out io.Writer) *cobra.Command {
......@@ -68,6 +82,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", "table", "Prints the output in the specified format (json|table|yaml)")
return cmd
}
......@@ -95,7 +110,12 @@ func (s *searchCmd) run(args []string) error {
return err
}
fmt.Fprintln(s.out, s.formatSearchResults(data, s.colWidth))
o, err := s.formatSearchResults(s.output, data, s.colWidth)
if err != nil {
return err
}
fmt.Fprintln(s.out, o)
return nil
}
......@@ -128,9 +148,14 @@ func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, err
return data, nil
}
func (s *searchCmd) formatSearchResults(res []*search.Result, colWidth uint) string {
func (s *searchCmd) formatSearchResults(format string, res []*search.Result, colWidth uint) (string, error) {
var output string
var err error
switch format {
case "table":
if len(res) == 0 {
return "No results found"
return "No results found", nil
}
table := uitable.New()
table.MaxColWidth = colWidth
......@@ -138,7 +163,38 @@ func (s *searchCmd) formatSearchResults(res []*search.Result, colWidth uint) str
for _, r := range res {
table.AddRow(r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description)
}
return table.String()
output = table.String()
case "json":
output, err = s.printFormated(format, res, json.Marshal)
case "yaml":
output, err = s.printFormated(format, res, yaml.Marshal)
}
return output, err
}
func (s *searchCmd) printFormated(format string, res []*search.Result, obj func(v interface{}) ([]byte, error)) (string, error) {
var sResult searchResult
var output string
var err error
if len(res) == 0 {
return "[]", nil
}
for _, r := range res {
sResult.Charts = append(sResult.Charts, &chartElement{r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description})
}
o, e := obj(sResult)
if e != nil {
err = fmt.Errorf("Failed to Marshal %s output: %s", strings.ToUpper(format), e)
} else {
output = string(o)
}
return output, err
}
func (s *searchCmd) buildIndex() (*search.Index, error) {
......
......@@ -18,6 +18,8 @@ package main
import (
"io"
"regexp"
"strings"
"testing"
"github.com/spf13/cobra"
......@@ -84,6 +86,30 @@ func TestSearchCmd(t *testing.T) {
flags: []string{"--regexp"},
err: true,
},
{
name: "search for 'maria', expect one match output json",
args: []string{"maria"},
flags: strings.Split("--output json", " "),
expected: regexp.QuoteMeta(`{"Charts":[{"Name":"testing/mariadb","Version":"0.3.0","AppVersion":"","Description":"Chart for MariaDB"}]}`),
},
{
name: "search for 'alpine', expect two matches output json",
args: []string{"alpine"},
flags: strings.Split("--output json", " "),
expected: regexp.QuoteMeta(`{"Charts":[{"Name":"testing/alpine","Version":"0.2.0","AppVersion":"2.3.4","Description":"Deploy a basic Alpine Linux pod"}]}`),
},
{
name: "search for 'maria', expect one match output yaml",
args: []string{"maria"},
flags: strings.Split("--output yaml", " "),
expected: "Charts:\n- AppVersion: \"\"\n Description: Chart for MariaDB\n Name: testing/mariadb\n Version: 0.3.0\n\n",
},
{
name: "search for 'alpine', expect two matches output yaml",
args: []string{"alpine"},
flags: strings.Split("--output yaml", " "),
expected: "Charts:\n- AppVersion: 2.3.4\n Description: Deploy a basic Alpine Linux pod\n Name: testing/alpine\n Version: 0.2.0\n\n",
},
}
cleanup := resetEnv()
......
......@@ -21,6 +21,7 @@ import (
"fmt"
"io"
"regexp"
"strings"
"text/tabwriter"
"github.com/ghodss/yaml"
......@@ -79,7 +80,7 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.Int32Var(&status.version, "revision", 0, "If set, display the status of the named release with revision")
f.StringVarP(&status.outfmt, "output", "o", "", "Output the status in the specified format (json or yaml)")
f.StringVarP(&status.outfmt, "output", "o", "table", "Prints the output in the specified format (json|table|yaml)")
// set defaults from environment
settings.InitTLS(f)
......@@ -93,56 +94,82 @@ func (s *statusCmd) run() error {
return prettyError(err)
}
switch s.outfmt {
case "":
PrintStatus(s.out, res)
return nil
case "json":
data, err := json.Marshal(res)
output, err := PrintStatusFormated(s.outfmt, res)
if err != nil {
return fmt.Errorf("Failed to Marshal JSON output: %s", err)
return err
}
s.out.Write(data)
fmt.Fprintf(s.out, output)
return nil
}
// PrintStatusFormated prints out the status of a release. Shared because also used by
// install / upgrade
func PrintStatusFormated(format string, res *services.GetReleaseStatusResponse) (string, error) {
var output string
var err error
switch format {
case "table":
output = printStatus(res)
case "json":
output, err = printFormatedReleaseStatus(format, res, json.Marshal)
case "yaml":
data, err := yaml.Marshal(res)
if err != nil {
return fmt.Errorf("Failed to Marshal YAML output: %s", err)
output, err = printFormatedReleaseStatus(format, res, yaml.Marshal)
default:
err = fmt.Errorf("Unknown output format %q", err)
}
s.out.Write(data)
return nil
return output, err
}
func printFormatedReleaseStatus(format string, res *services.GetReleaseStatusResponse, obj func(v interface{}) ([]byte, error)) (string, error) {
var output string
var err error
o, err := obj(res)
if err != nil {
return "", fmt.Errorf("Failed to Marshal %s output: %s", strings.ToUpper(format), err)
}
output = string(o)
return fmt.Errorf("Unknown output format %q", s.outfmt)
return output, err
}
// PrintStatus prints out the status of a release. Shared because also used by
// install / upgrade
func PrintStatus(out io.Writer, res *services.GetReleaseStatusResponse) {
func printStatus(res *services.GetReleaseStatusResponse) string {
var out strings.Builder
if res.Info.LastDeployed != nil {
fmt.Fprintf(out, "LAST DEPLOYED: %s\n", timeconv.String(res.Info.LastDeployed))
fmt.Fprintf(&out, "LAST DEPLOYED: %s\n", timeconv.String(res.Info.LastDeployed))
}
fmt.Fprintf(out, "NAMESPACE: %s\n", res.Namespace)
fmt.Fprintf(out, "STATUS: %s\n", res.Info.Status.Code)
fmt.Fprintf(out, "\n")
fmt.Fprintf(&out, "NAMESPACE: %s\n", res.Namespace)
fmt.Fprintf(&out, "STATUS: %s\n", res.Info.Status.Code)
fmt.Fprintf(&out, "\n")
if len(res.Info.Status.Resources) > 0 {
re := regexp.MustCompile(" +")
w := tabwriter.NewWriter(out, 0, 0, 2, ' ', tabwriter.TabIndent)
w := tabwriter.NewWriter(&out, 0, 0, 2, ' ', tabwriter.TabIndent)
fmt.Fprintf(w, "RESOURCES:\n%s\n", re.ReplaceAllString(res.Info.Status.Resources, "\t"))
w.Flush()
}
if res.Info.Status.LastTestSuiteRun != nil {
lastRun := res.Info.Status.LastTestSuiteRun
fmt.Fprintf(out, "TEST SUITE:\n%s\n%s\n\n%s\n",
fmt.Fprintf(&out, "TEST SUITE:\n%s\n%s\n\n%s\n",
fmt.Sprintf("Last Started: %s", timeconv.String(lastRun.StartedAt)),
fmt.Sprintf("Last Completed: %s", timeconv.String(lastRun.CompletedAt)),
formatTestResults(lastRun.Results))
}
if len(res.Info.Status.Notes) > 0 {
fmt.Fprintf(out, "NOTES:\n%s\n", res.Info.Status.Notes)
fmt.Fprintf(&out, "NOTES:\n%s\n", res.Info.Status.Notes)
}
return out.String()
}
func formatTestResults(results []*release.TestRun) string {
......
......@@ -117,6 +117,7 @@ type upgradeCmd struct {
certFile string
keyFile string
caFile string
output string
}
func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
......@@ -181,6 +182,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&upgrade.subNotes, "render-subchart-notes", false, "Render subchart notes along with parent")
f.StringVar(&upgrade.description, "description", "", "Specify the description to use for the upgrade, rather than the default")
f.BoolVar(&upgrade.cleanupOnFail, "cleanup-on-fail", false, "Allow deletion of new resources created in this upgrade when upgrade failed")
f.StringVarP(&upgrade.output, "output", "o", "table", "Prints the output in the specified format (json|table|yaml)")
f.MarkDeprecated("disable-hooks", "Use --no-hooks instead")
......@@ -307,14 +309,22 @@ func (u *upgradeCmd) run() error {
printRelease(u.out, resp.Release)
}
if u.output == "table" {
fmt.Fprintf(u.out, "Release %q has been upgraded.\n", u.release)
}
// Print the status like status command does
status, err := u.client.ReleaseStatus(u.release)
if err != nil {
return prettyError(err)
}
PrintStatus(u.out, status)
output, err := PrintStatusFormated(u.output, status)
if err != nil {
return err
}
fmt.Fprintf(u.out, output)
return nil
}
......@@ -93,6 +93,7 @@ helm install [CHART] [flags]
--namespace string Namespace to install the release into. Defaults to the current kube config namespace.
--no-crd-hook Prevent CRD hooks from running, but run other hooks
--no-hooks Prevent hooks from running during install
-o, --output string Prints the output in the specified format (json|table|yaml) (default "table")
--password string Chart repository password where to locate the requested chart
--render-subchart-notes Render subchart notes along with the parent
--replace Re-use the given name, even if that name is already used. This is unsafe in production
......@@ -130,4 +131,4 @@ helm install [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 16-May-2019
###### Auto generated by spf13/cobra on 24-Sep-2019
......@@ -14,6 +14,7 @@ helm repo list [flags]
```
-h, --help help for list
-o, --output string Prints the output in the specified format (json|table|yaml) (default "table")
```
### Options inherited from parent commands
......@@ -32,4 +33,4 @@ helm repo list [flags]
* [helm repo](helm_repo.md) - Add, list, remove, update, and index chart repositories
###### Auto generated by spf13/cobra on 16-May-2019
###### Auto generated by spf13/cobra on 24-Sep-2019
......@@ -20,6 +20,7 @@ helm search [keyword] [flags]
```
--col-width uint Specifies the max column width of output (default 60)
-h, --help help for search
-o, --output string Prints the output in the specified format (json|table|yaml) (default "table")
-r, --regexp Use regular expressions for searching
-v, --version string Search using semantic versioning constraints
-l, --versions Show the long listing, with each version of each chart on its own line
......@@ -41,4 +42,4 @@ helm search [keyword] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 16-May-2019
###### Auto generated by spf13/cobra on 24-Sep-2019
......@@ -23,7 +23,7 @@ helm status [flags] RELEASE_NAME
```
-h, --help help for status
-o, --output string Output the status in the specified format (json or yaml)
-o, --output string Prints the output in the specified format (json|table|yaml) (default "table")
--revision int32 If set, display the status of the named release with revision
--tls Enable TLS for request
--tls-ca-cert string Path to TLS CA certificate file (default "$HELM_HOME/ca.pem")
......@@ -49,4 +49,4 @@ helm status [flags] RELEASE_NAME
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 16-May-2019
###### Auto generated by spf13/cobra on 6-Sep-2019
......@@ -79,6 +79,7 @@ helm upgrade [RELEASE] [CHART] [flags]
--keyring string Path to the keyring that contains public signing keys (default "~/.gnupg/pubring.gpg")
--namespace string Namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace
--no-hooks Disable pre/post upgrade hooks
-o, --output string Prints the output in the specified format (json|table|yaml) (default "table")
--password string Chart repository password where to locate the requested chart
--recreate-pods Performs pods restart for the resource if applicable
--render-subchart-notes Render subchart notes along with parent
......@@ -118,4 +119,4 @@ helm upgrade [RELEASE] [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 16-May-2019
###### Auto generated by spf13/cobra on 24-Sep-2019
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment