github.com/valdemarpavesi/helm@v2.9.1+incompatible/cmd/helm/list.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors All rights reserved.
     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  	"strings"
    23  
    24  	"github.com/gosuri/uitable"
    25  	"github.com/spf13/cobra"
    26  
    27  	"k8s.io/helm/pkg/helm"
    28  	"k8s.io/helm/pkg/proto/hapi/release"
    29  	"k8s.io/helm/pkg/proto/hapi/services"
    30  	"k8s.io/helm/pkg/timeconv"
    31  )
    32  
    33  var listHelp = `
    34  This command lists all of the releases.
    35  
    36  By default, it lists only releases that are deployed or failed. Flags like
    37  '--deleted' and '--all' will alter this behavior. Such flags can be combined:
    38  '--deleted --failed'.
    39  
    40  By default, items are sorted alphabetically. Use the '-d' flag to sort by
    41  release date.
    42  
    43  If an argument is provided, it will be treated as a filter. Filters are
    44  regular expressions (Perl compatible) that are applied to the list of releases.
    45  Only items that match the filter will be returned.
    46  
    47  	$ helm list 'ara[a-z]+'
    48  	NAME            	UPDATED                 	CHART
    49  	maudlin-arachnid	Mon May  9 16:07:08 2016	alpine-0.1.0
    50  
    51  If no results are found, 'helm list' will exit 0, but with no output (or in
    52  the case of no '-q' flag, only headers).
    53  
    54  By default, up to 256 items may be returned. To limit this, use the '--max' flag.
    55  Setting '--max' to 0 will not return all results. Rather, it will return the
    56  server's default, which may be much higher than 256. Pairing the '--max'
    57  flag with the '--offset' flag allows you to page through results.
    58  `
    59  
    60  type listCmd struct {
    61  	filter     string
    62  	short      bool
    63  	limit      int
    64  	offset     string
    65  	byDate     bool
    66  	sortDesc   bool
    67  	out        io.Writer
    68  	all        bool
    69  	deleted    bool
    70  	deleting   bool
    71  	deployed   bool
    72  	failed     bool
    73  	namespace  string
    74  	superseded bool
    75  	pending    bool
    76  	client     helm.Interface
    77  	colWidth   uint
    78  }
    79  
    80  func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
    81  	list := &listCmd{
    82  		out:    out,
    83  		client: client,
    84  	}
    85  
    86  	cmd := &cobra.Command{
    87  		Use:     "list [flags] [FILTER]",
    88  		Short:   "list releases",
    89  		Long:    listHelp,
    90  		Aliases: []string{"ls"},
    91  		PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
    92  		RunE: func(cmd *cobra.Command, args []string) error {
    93  			if len(args) > 0 {
    94  				list.filter = strings.Join(args, " ")
    95  			}
    96  			if list.client == nil {
    97  				list.client = newClient()
    98  			}
    99  			return list.run()
   100  		},
   101  	}
   102  
   103  	f := cmd.Flags()
   104  	f.BoolVarP(&list.short, "short", "q", false, "output short (quiet) listing format")
   105  	f.BoolVarP(&list.byDate, "date", "d", false, "sort by release date")
   106  	f.BoolVarP(&list.sortDesc, "reverse", "r", false, "reverse the sort order")
   107  	f.IntVarP(&list.limit, "max", "m", 256, "maximum number of releases to fetch")
   108  	f.StringVarP(&list.offset, "offset", "o", "", "next release name in the list, used to offset from start value")
   109  	f.BoolVarP(&list.all, "all", "a", false, "show all releases, not just the ones marked DEPLOYED")
   110  	f.BoolVar(&list.deleted, "deleted", false, "show deleted releases")
   111  	f.BoolVar(&list.deleting, "deleting", false, "show releases that are currently being deleted")
   112  	f.BoolVar(&list.deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
   113  	f.BoolVar(&list.failed, "failed", false, "show failed releases")
   114  	f.BoolVar(&list.pending, "pending", false, "show pending releases")
   115  	f.StringVar(&list.namespace, "namespace", "", "show releases within a specific namespace")
   116  	f.UintVar(&list.colWidth, "col-width", 60, "specifies the max column width of output")
   117  
   118  	// TODO: Do we want this as a feature of 'helm list'?
   119  	//f.BoolVar(&list.superseded, "history", true, "show historical releases")
   120  
   121  	return cmd
   122  }
   123  
   124  func (l *listCmd) run() error {
   125  	sortBy := services.ListSort_NAME
   126  	if l.byDate {
   127  		sortBy = services.ListSort_LAST_RELEASED
   128  	}
   129  
   130  	sortOrder := services.ListSort_ASC
   131  	if l.sortDesc {
   132  		sortOrder = services.ListSort_DESC
   133  	}
   134  
   135  	stats := l.statusCodes()
   136  
   137  	res, err := l.client.ListReleases(
   138  		helm.ReleaseListLimit(l.limit),
   139  		helm.ReleaseListOffset(l.offset),
   140  		helm.ReleaseListFilter(l.filter),
   141  		helm.ReleaseListSort(int32(sortBy)),
   142  		helm.ReleaseListOrder(int32(sortOrder)),
   143  		helm.ReleaseListStatuses(stats),
   144  		helm.ReleaseListNamespace(l.namespace),
   145  	)
   146  
   147  	if err != nil {
   148  		return prettyError(err)
   149  	}
   150  
   151  	if len(res.GetReleases()) == 0 {
   152  		return nil
   153  	}
   154  
   155  	if res.Next != "" && !l.short {
   156  		fmt.Fprintf(l.out, "\tnext: %s\n", res.Next)
   157  	}
   158  
   159  	rels := filterList(res.Releases)
   160  
   161  	if l.short {
   162  		for _, r := range rels {
   163  			fmt.Fprintln(l.out, r.Name)
   164  		}
   165  		return nil
   166  	}
   167  	fmt.Fprintln(l.out, formatList(rels, l.colWidth))
   168  	return nil
   169  }
   170  
   171  // filterList returns a list scrubbed of old releases.
   172  func filterList(rels []*release.Release) []*release.Release {
   173  	idx := map[string]int32{}
   174  
   175  	for _, r := range rels {
   176  		name, version := r.GetName(), r.GetVersion()
   177  		if max, ok := idx[name]; ok {
   178  			// check if we have a greater version already
   179  			if max > version {
   180  				continue
   181  			}
   182  		}
   183  		idx[name] = version
   184  	}
   185  
   186  	uniq := make([]*release.Release, 0, len(idx))
   187  	for _, r := range rels {
   188  		if idx[r.GetName()] == r.GetVersion() {
   189  			uniq = append(uniq, r)
   190  		}
   191  	}
   192  	return uniq
   193  }
   194  
   195  // statusCodes gets the list of status codes that are to be included in the results.
   196  func (l *listCmd) statusCodes() []release.Status_Code {
   197  	if l.all {
   198  		return []release.Status_Code{
   199  			release.Status_UNKNOWN,
   200  			release.Status_DEPLOYED,
   201  			release.Status_DELETED,
   202  			release.Status_DELETING,
   203  			release.Status_FAILED,
   204  			release.Status_PENDING_INSTALL,
   205  			release.Status_PENDING_UPGRADE,
   206  			release.Status_PENDING_ROLLBACK,
   207  		}
   208  	}
   209  	status := []release.Status_Code{}
   210  	if l.deployed {
   211  		status = append(status, release.Status_DEPLOYED)
   212  	}
   213  	if l.deleted {
   214  		status = append(status, release.Status_DELETED)
   215  	}
   216  	if l.deleting {
   217  		status = append(status, release.Status_DELETING)
   218  	}
   219  	if l.failed {
   220  		status = append(status, release.Status_FAILED)
   221  	}
   222  	if l.superseded {
   223  		status = append(status, release.Status_SUPERSEDED)
   224  	}
   225  	if l.pending {
   226  		status = append(status, release.Status_PENDING_INSTALL, release.Status_PENDING_UPGRADE, release.Status_PENDING_ROLLBACK)
   227  	}
   228  
   229  	// Default case.
   230  	if len(status) == 0 {
   231  		status = append(status, release.Status_DEPLOYED, release.Status_FAILED)
   232  	}
   233  	return status
   234  }
   235  
   236  func formatList(rels []*release.Release, colWidth uint) string {
   237  	table := uitable.New()
   238  
   239  	table.MaxColWidth = colWidth
   240  	table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "NAMESPACE")
   241  	for _, r := range rels {
   242  		md := r.GetChart().GetMetadata()
   243  		c := fmt.Sprintf("%s-%s", md.GetName(), md.GetVersion())
   244  		t := "-"
   245  		if tspb := r.GetInfo().GetLastDeployed(); tspb != nil {
   246  			t = timeconv.String(tspb)
   247  		}
   248  		s := r.GetInfo().GetStatus().GetCode().String()
   249  		v := r.GetVersion()
   250  		n := r.GetNamespace()
   251  		table.AddRow(r.GetName(), v, t, s, c, n)
   252  	}
   253  	return table.String()
   254  }