github.com/wangchanggan/helm@v0.0.0-20211020154240-11b1b7d5406d/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  	"encoding/json"
    21  	"fmt"
    22  	"io"
    23  	"strings"
    24  
    25  	"github.com/ghodss/yaml"
    26  	"github.com/gosuri/uitable"
    27  	"github.com/spf13/cobra"
    28  
    29  	"k8s.io/helm/pkg/helm"
    30  	"k8s.io/helm/pkg/proto/hapi/release"
    31  	"k8s.io/helm/pkg/proto/hapi/services"
    32  	"k8s.io/helm/pkg/timeconv"
    33  )
    34  
    35  var listHelp = `
    36  This command lists all of the releases.
    37  
    38  By default, it lists only releases that are deployed or failed. Flags like
    39  '--deleted' and '--all' will alter this behavior. Such flags can be combined:
    40  '--deleted --failed'.
    41  
    42  By default, items are sorted alphabetically. Use the '-d' flag to sort by
    43  release date.
    44  
    45  If an argument is provided, it will be treated as a filter. Filters are
    46  regular expressions (Perl compatible) that are applied to the list of releases.
    47  Only items that match the filter will be returned.
    48  
    49  	$ helm list 'ara[a-z]+'
    50  	NAME            	UPDATED                 	CHART
    51  	maudlin-arachnid	Mon May  9 16:07:08 2016	alpine-0.1.0
    52  
    53  If no results are found, 'helm list' will exit 0, but with no output (or in
    54  the case of no '-q' flag, only headers).
    55  
    56  By default, up to 256 items may be returned. To limit this, use the '--max' flag.
    57  Setting '--max' to 0 will not return all results. Rather, it will return the
    58  server's default, which may be much higher than 256. Pairing the '--max'
    59  flag with the '--offset' flag allows you to page through results.
    60  `
    61  
    62  type listCmd struct {
    63  	filter      string
    64  	short       bool
    65  	limit       int
    66  	offset      string
    67  	byDate      bool
    68  	sortDesc    bool
    69  	out         io.Writer
    70  	all         bool
    71  	deleted     bool
    72  	deleting    bool
    73  	deployed    bool
    74  	failed      bool
    75  	namespace   string
    76  	superseded  bool
    77  	pending     bool
    78  	client      helm.Interface
    79  	colWidth    uint
    80  	output      string
    81  	byChartName bool
    82  }
    83  
    84  type listResult struct {
    85  	Next     string
    86  	Releases []listRelease
    87  }
    88  
    89  type listRelease struct {
    90  	Name       string
    91  	Revision   int32
    92  	Updated    string
    93  	Status     string
    94  	Chart      string
    95  	AppVersion string
    96  	Namespace  string
    97  }
    98  
    99  func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
   100  	list := &listCmd{
   101  		out:    out,
   102  		client: client,
   103  	}
   104  
   105  	cmd := &cobra.Command{
   106  		Use:     "list [flags] [FILTER]",
   107  		Short:   "List releases",
   108  		Long:    listHelp,
   109  		Aliases: []string{"ls"},
   110  		PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
   111  		RunE: func(cmd *cobra.Command, args []string) error {
   112  			if len(args) > 0 {
   113  				list.filter = strings.Join(args, " ")
   114  			}
   115  			if list.client == nil {
   116  				list.client = newClient()
   117  			}
   118  			return list.run()
   119  		},
   120  	}
   121  
   122  	f := cmd.Flags()
   123  	settings.AddFlagsTLS(f)
   124  	f.BoolVarP(&list.short, "short", "q", false, "Output short (quiet) listing format")
   125  	f.BoolVarP(&list.byDate, "date", "d", false, "Sort by release date")
   126  	f.BoolVarP(&list.sortDesc, "reverse", "r", false, "Reverse the sort order")
   127  	f.IntVarP(&list.limit, "max", "m", 256, "Maximum number of releases to fetch")
   128  	f.StringVarP(&list.offset, "offset", "o", "", "Next release name in the list, used to offset from start value")
   129  	f.BoolVarP(&list.all, "all", "a", false, "Show all releases, not just the ones marked DEPLOYED")
   130  	f.BoolVar(&list.deleted, "deleted", false, "Show deleted releases")
   131  	f.BoolVar(&list.deleting, "deleting", false, "Show releases that are currently being deleted")
   132  	f.BoolVar(&list.deployed, "deployed", false, "Show deployed releases. If no other is specified, this will be automatically enabled")
   133  	f.BoolVar(&list.failed, "failed", false, "Show failed releases")
   134  	f.BoolVar(&list.pending, "pending", false, "Show pending releases")
   135  	f.StringVar(&list.namespace, "namespace", "", "Show releases within a specific namespace")
   136  	f.UintVar(&list.colWidth, "col-width", 60, "Specifies the max column width of output")
   137  	f.StringVar(&list.output, "output", "", "Output the specified format (json or yaml)")
   138  	f.BoolVarP(&list.byChartName, "chart-name", "c", false, "Sort by chart name")
   139  
   140  	// TODO: Do we want this as a feature of 'helm list'?
   141  	//f.BoolVar(&list.superseded, "history", true, "show historical releases")
   142  
   143  	// set defaults from environment
   144  	settings.InitTLS(f)
   145  
   146  	return cmd
   147  }
   148  
   149  func (l *listCmd) run() error {
   150  	// 状态和查询参数设置, 主要目的是限制每次查询返回结果的数量,然后规定以哪种方式对结果排序
   151  	// 同时还支持按照状态来返回在询的结果,设置根据命名空间查询
   152  	sortBy := services.ListSort_NAME
   153  	if l.byDate {
   154  		sortBy = services.ListSort_LAST_RELEASED
   155  	}
   156  	if l.byChartName {
   157  		sortBy = services.ListSort_CHART_NAME
   158  	}
   159  
   160  	sortOrder := services.ListSort_ASC
   161  	if l.sortDesc {
   162  		sortOrder = services.ListSort_DESC
   163  	}
   164  
   165  	stats := l.statusCodes()
   166  
   167  	res, err := l.client.ListReleases(
   168  		helm.ReleaseListLimit(l.limit),
   169  		helm.ReleaseListOffset(l.offset),
   170  		helm.ReleaseListFilter(l.filter),
   171  		helm.ReleaseListSort(int32(sortBy)),
   172  		helm.ReleaseListOrder(int32(sortOrder)),
   173  		helm.ReleaseListStatuses(stats),
   174  		helm.ReleaseListNamespace(l.namespace),
   175  	)
   176  
   177  	if err != nil {
   178  		return prettyError(err)
   179  	}
   180  	if res == nil {
   181  		return nil
   182  	}
   183  
   184  	rels := filterList(res.GetReleases())
   185  
   186  	result := getListResult(rels, res.Next)
   187  
   188  	output, err := formatResult(l.output, l.short, result, l.colWidth)
   189  
   190  	if err != nil {
   191  		return prettyError(err)
   192  	}
   193  
   194  	fmt.Fprintln(l.out, output)
   195  	return nil
   196  }
   197  
   198  // filterList returns a list scrubbed of old releases.
   199  func filterList(rels []*release.Release) []*release.Release {
   200  	idx := map[string]int32{}
   201  
   202  	for _, r := range rels {
   203  		name, version := r.GetName(), r.GetVersion()
   204  		if max, ok := idx[name]; ok {
   205  			// check if we have a greater version already
   206  			if max > version {
   207  				continue
   208  			}
   209  		}
   210  		idx[name] = version
   211  	}
   212  
   213  	uniq := make([]*release.Release, 0, len(idx))
   214  	for _, r := range rels {
   215  		if idx[r.GetName()] == r.GetVersion() {
   216  			uniq = append(uniq, r)
   217  		}
   218  	}
   219  	return uniq
   220  }
   221  
   222  // statusCodes gets the list of status codes that are to be included in the results.
   223  func (l *listCmd) statusCodes() []release.Status_Code {
   224  	if l.all {
   225  		return []release.Status_Code{
   226  			release.Status_UNKNOWN,
   227  			release.Status_DEPLOYED,
   228  			release.Status_DELETED,
   229  			release.Status_DELETING,
   230  			release.Status_FAILED,
   231  			release.Status_PENDING_INSTALL,
   232  			release.Status_PENDING_UPGRADE,
   233  			release.Status_PENDING_ROLLBACK,
   234  		}
   235  	}
   236  	status := []release.Status_Code{}
   237  	if l.deployed {
   238  		status = append(status, release.Status_DEPLOYED)
   239  	}
   240  	if l.deleted {
   241  		status = append(status, release.Status_DELETED)
   242  	}
   243  	if l.deleting {
   244  		status = append(status, release.Status_DELETING)
   245  	}
   246  	if l.failed {
   247  		status = append(status, release.Status_FAILED)
   248  	}
   249  	if l.superseded {
   250  		status = append(status, release.Status_SUPERSEDED)
   251  	}
   252  	if l.pending {
   253  		status = append(status, release.Status_PENDING_INSTALL, release.Status_PENDING_UPGRADE, release.Status_PENDING_ROLLBACK)
   254  	}
   255  
   256  	// Default case.
   257  	if len(status) == 0 {
   258  		status = append(status, release.Status_DEPLOYED, release.Status_FAILED)
   259  	}
   260  	return status
   261  }
   262  
   263  func getListResult(rels []*release.Release, next string) listResult {
   264  	listReleases := []listRelease{}
   265  	for _, r := range rels {
   266  		md := r.GetChart().GetMetadata()
   267  		t := "-"
   268  		if tspb := r.GetInfo().GetLastDeployed(); tspb != nil {
   269  			t = timeconv.String(tspb)
   270  		}
   271  
   272  		lr := listRelease{
   273  			Name:       r.GetName(),
   274  			Revision:   r.GetVersion(),
   275  			Updated:    t,
   276  			Status:     r.GetInfo().GetStatus().GetCode().String(),
   277  			Chart:      fmt.Sprintf("%s-%s", md.GetName(), md.GetVersion()),
   278  			AppVersion: md.GetAppVersion(),
   279  			Namespace:  r.GetNamespace(),
   280  		}
   281  		listReleases = append(listReleases, lr)
   282  	}
   283  
   284  	return listResult{
   285  		Releases: listReleases,
   286  		Next:     next,
   287  	}
   288  }
   289  
   290  func shortenListResult(result listResult) []string {
   291  	names := []string{}
   292  	for _, r := range result.Releases {
   293  		names = append(names, r.Name)
   294  	}
   295  
   296  	return names
   297  }
   298  
   299  func formatResult(format string, short bool, result listResult, colWidth uint) (string, error) {
   300  	var output string
   301  	var err error
   302  
   303  	var shortResult []string
   304  	var finalResult interface{}
   305  	if short {
   306  		shortResult = shortenListResult(result)
   307  		finalResult = shortResult
   308  	} else {
   309  		finalResult = result
   310  	}
   311  
   312  	switch format {
   313  	case "":
   314  		if short {
   315  			output = formatTextShort(shortResult)
   316  		} else {
   317  			output = formatText(result, colWidth)
   318  		}
   319  	case "json":
   320  		o, e := json.Marshal(finalResult)
   321  		if e != nil {
   322  			err = fmt.Errorf("Failed to Marshal JSON output: %s", e)
   323  		} else {
   324  			output = string(o)
   325  		}
   326  	case "yaml":
   327  		o, e := yaml.Marshal(finalResult)
   328  		if e != nil {
   329  			err = fmt.Errorf("Failed to Marshal YAML output: %s", e)
   330  		} else {
   331  			output = string(o)
   332  		}
   333  	default:
   334  		err = fmt.Errorf("Unknown output format \"%s\"", format)
   335  	}
   336  	return output, err
   337  }
   338  
   339  func formatText(result listResult, colWidth uint) string {
   340  	nextOutput := ""
   341  	if result.Next != "" {
   342  		nextOutput = fmt.Sprintf("\tnext: %s\n", result.Next)
   343  	}
   344  
   345  	table := uitable.New()
   346  	table.MaxColWidth = colWidth
   347  	table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION", "NAMESPACE")
   348  	for _, lr := range result.Releases {
   349  		table.AddRow(lr.Name, lr.Revision, lr.Updated, lr.Status, lr.Chart, lr.AppVersion, lr.Namespace)
   350  	}
   351  
   352  	return fmt.Sprintf("%s%s", nextOutput, table.String())
   353  }
   354  
   355  func formatTextShort(shortResult []string) string {
   356  	return strings.Join(shortResult, "\n")
   357  }