github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/cmd/helm/list.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"strconv"
    24  
    25  	"github.com/gosuri/uitable"
    26  	"github.com/spf13/cobra"
    27  
    28  	"github.com/stefanmcshane/helm/cmd/helm/require"
    29  	"github.com/stefanmcshane/helm/pkg/action"
    30  	"github.com/stefanmcshane/helm/pkg/cli/output"
    31  	"github.com/stefanmcshane/helm/pkg/release"
    32  )
    33  
    34  var listHelp = `
    35  This command lists all of the releases for a specified namespace (uses current namespace context if namespace not specified).
    36  
    37  By default, it lists only releases that are deployed or failed. Flags like
    38  '--uninstalled' and '--all' will alter this behavior. Such flags can be combined:
    39  '--uninstalled --failed'.
    40  
    41  By default, items are sorted alphabetically. Use the '-d' flag to sort by
    42  release date.
    43  
    44  If the --filter flag is provided, it will be treated as a filter. Filters are
    45  regular expressions (Perl compatible) that are applied to the list of releases.
    46  Only items that match the filter will be returned.
    47  
    48      $ helm list --filter 'ara[a-z]+'
    49      NAME                UPDATED                                  CHART
    50      maudlin-arachnid    2020-06-18 14:17:46.125134977 +0000 UTC  alpine-0.1.0
    51  
    52  If no results are found, 'helm list' will exit 0, but with no output (or in
    53  the case of no '-q' flag, only headers).
    54  
    55  By default, up to 256 items may be returned. To limit this, use the '--max' flag.
    56  Setting '--max' to 0 will not return all results. Rather, it will return the
    57  server's default, which may be much higher than 256. Pairing the '--max'
    58  flag with the '--offset' flag allows you to page through results.
    59  `
    60  
    61  func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
    62  	client := action.NewList(cfg)
    63  	var outfmt output.Format
    64  
    65  	cmd := &cobra.Command{
    66  		Use:               "list",
    67  		Short:             "list releases",
    68  		Long:              listHelp,
    69  		Aliases:           []string{"ls"},
    70  		Args:              require.NoArgs,
    71  		ValidArgsFunction: noCompletions,
    72  		RunE: func(cmd *cobra.Command, args []string) error {
    73  			if client.AllNamespaces {
    74  				if err := cfg.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), debug); err != nil {
    75  					return err
    76  				}
    77  			}
    78  			client.SetStateMask()
    79  
    80  			results, err := client.Run()
    81  			if err != nil {
    82  				return err
    83  			}
    84  
    85  			if client.Short {
    86  				names := make([]string, 0, len(results))
    87  				for _, res := range results {
    88  					names = append(names, res.Name)
    89  				}
    90  
    91  				outputFlag := cmd.Flag("output")
    92  
    93  				switch outputFlag.Value.String() {
    94  				case "json":
    95  					output.EncodeJSON(out, names)
    96  					return nil
    97  				case "yaml":
    98  					output.EncodeYAML(out, names)
    99  					return nil
   100  				case "table":
   101  					for _, res := range results {
   102  						fmt.Fprintln(out, res.Name)
   103  					}
   104  					return nil
   105  				}
   106  			}
   107  
   108  			return outfmt.Write(out, newReleaseListWriter(results, client.TimeFormat, client.NoHeaders))
   109  		},
   110  	}
   111  
   112  	f := cmd.Flags()
   113  	f.BoolVarP(&client.Short, "short", "q", false, "output short (quiet) listing format")
   114  	f.BoolVarP(&client.NoHeaders, "no-headers", "", false, "don't print headers when using the default output format")
   115  	f.StringVar(&client.TimeFormat, "time-format", "", `format time using golang time formatter. Example: --time-format "2006-01-02 15:04:05Z0700"`)
   116  	f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date")
   117  	f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order")
   118  	f.BoolVarP(&client.All, "all", "a", false, "show all releases without any filter applied")
   119  	f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases (if 'helm uninstall --keep-history' was used)")
   120  	f.BoolVar(&client.Superseded, "superseded", false, "show superseded releases")
   121  	f.BoolVar(&client.Uninstalling, "uninstalling", false, "show releases that are currently being uninstalled")
   122  	f.BoolVar(&client.Deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
   123  	f.BoolVar(&client.Failed, "failed", false, "show failed releases")
   124  	f.BoolVar(&client.Pending, "pending", false, "show pending releases")
   125  	f.BoolVarP(&client.AllNamespaces, "all-namespaces", "A", false, "list releases across all namespaces")
   126  	f.IntVarP(&client.Limit, "max", "m", 256, "maximum number of releases to fetch")
   127  	f.IntVar(&client.Offset, "offset", 0, "next release index in the list, used to offset from start value")
   128  	f.StringVarP(&client.Filter, "filter", "f", "", "a regular expression (Perl compatible). Any releases that match the expression will be included in the results")
   129  	f.StringVarP(&client.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Works only for secret(default) and configmap storage backends.")
   130  	bindOutputFlag(cmd, &outfmt)
   131  
   132  	return cmd
   133  }
   134  
   135  type releaseElement struct {
   136  	Name       string `json:"name"`
   137  	Namespace  string `json:"namespace"`
   138  	Revision   string `json:"revision"`
   139  	Updated    string `json:"updated"`
   140  	Status     string `json:"status"`
   141  	Chart      string `json:"chart"`
   142  	AppVersion string `json:"app_version"`
   143  }
   144  
   145  type releaseListWriter struct {
   146  	releases  []releaseElement
   147  	noHeaders bool
   148  }
   149  
   150  func newReleaseListWriter(releases []*release.Release, timeFormat string, noHeaders bool) *releaseListWriter {
   151  	// Initialize the array so no results returns an empty array instead of null
   152  	elements := make([]releaseElement, 0, len(releases))
   153  	for _, r := range releases {
   154  		element := releaseElement{
   155  			Name:       r.Name,
   156  			Namespace:  r.Namespace,
   157  			Revision:   strconv.Itoa(r.Version),
   158  			Status:     r.Info.Status.String(),
   159  			Chart:      formatChartname(r.Chart),
   160  			AppVersion: formatAppVersion(r.Chart),
   161  		}
   162  
   163  		t := "-"
   164  		if tspb := r.Info.LastDeployed; !tspb.IsZero() {
   165  			if timeFormat != "" {
   166  				t = tspb.Format(timeFormat)
   167  			} else {
   168  				t = tspb.String()
   169  			}
   170  		}
   171  		element.Updated = t
   172  
   173  		elements = append(elements, element)
   174  	}
   175  	return &releaseListWriter{elements, noHeaders}
   176  }
   177  
   178  func (r *releaseListWriter) WriteTable(out io.Writer) error {
   179  	table := uitable.New()
   180  	if !r.noHeaders {
   181  		table.AddRow("NAME", "NAMESPACE", "REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION")
   182  	}
   183  	for _, r := range r.releases {
   184  		table.AddRow(r.Name, r.Namespace, r.Revision, r.Updated, r.Status, r.Chart, r.AppVersion)
   185  	}
   186  	return output.EncodeTable(out, table)
   187  }
   188  
   189  func (r *releaseListWriter) WriteJSON(out io.Writer) error {
   190  	return output.EncodeJSON(out, r.releases)
   191  }
   192  
   193  func (r *releaseListWriter) WriteYAML(out io.Writer) error {
   194  	return output.EncodeYAML(out, r.releases)
   195  }
   196  
   197  // Returns all releases from 'releases', except those with names matching 'ignoredReleases'
   198  func filterReleases(releases []*release.Release, ignoredReleaseNames []string) []*release.Release {
   199  	// if ignoredReleaseNames is nil, just return releases
   200  	if ignoredReleaseNames == nil {
   201  		return releases
   202  	}
   203  
   204  	var filteredReleases []*release.Release
   205  	for _, rel := range releases {
   206  		found := false
   207  		for _, ignoredName := range ignoredReleaseNames {
   208  			if rel.Name == ignoredName {
   209  				found = true
   210  				break
   211  			}
   212  		}
   213  		if !found {
   214  			filteredReleases = append(filteredReleases, rel)
   215  		}
   216  	}
   217  
   218  	return filteredReleases
   219  }
   220  
   221  // Provide dynamic auto-completion for release names
   222  func compListReleases(toComplete string, ignoredReleaseNames []string, cfg *action.Configuration) ([]string, cobra.ShellCompDirective) {
   223  	cobra.CompDebugln(fmt.Sprintf("compListReleases with toComplete %s", toComplete), settings.Debug)
   224  
   225  	client := action.NewList(cfg)
   226  	client.All = true
   227  	client.Limit = 0
   228  	// Do not filter so as to get the entire list of releases.
   229  	// This will allow zsh and fish to match completion choices
   230  	// on other criteria then prefix.  For example:
   231  	//   helm status ingress<TAB>
   232  	// can match
   233  	//   helm status nginx-ingress
   234  	//
   235  	// client.Filter = fmt.Sprintf("^%s", toComplete)
   236  
   237  	client.SetStateMask()
   238  	releases, err := client.Run()
   239  	if err != nil {
   240  		return nil, cobra.ShellCompDirectiveDefault
   241  	}
   242  
   243  	var choices []string
   244  	filteredReleases := filterReleases(releases, ignoredReleaseNames)
   245  	for _, rel := range filteredReleases {
   246  		choices = append(choices,
   247  			fmt.Sprintf("%s\t%s-%s -> %s", rel.Name, rel.Chart.Metadata.Name, rel.Chart.Metadata.Version, rel.Info.Status.String()))
   248  	}
   249  
   250  	return choices, cobra.ShellCompDirectiveNoFileComp
   251  }