github.com/danielqsj/helm@v2.0.0-alpha.4.0.20160908204436-976e0ba5199b+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. Flags like '--delete' and
    37  '--all' will alter this behavior. Such flags can be combined: '--deleted --failed'.
    38  
    39  By default, items are sorted alphabetically. Use the '-d' flag to sort by
    40  release date.
    41  
    42  If an argument is provided, it will be treated as a filter. Filters are
    43  regular expressions (Perl compatible) that are applied to the list of releases.
    44  Only items that match the filter will be returned.
    45  
    46  	$ helm list -l 'ara[a-z]+'
    47  	NAME            	UPDATED                 	CHART
    48  	maudlin-arachnid	Mon May  9 16:07:08 2016	alpine-0.1.0
    49  
    50  If no results are found, 'helm list' will exit 0, but with no output (or in
    51  the case of '-l', only headers).
    52  
    53  By default, up to 256 items may be returned. To limit this, use the '--max' flag.
    54  Setting '--max' to 0 will not return all results. Rather, it will return the
    55  server's default, which may be much higher than 256. Pairing the '--max'
    56  flag with the '--offset' flag allows you to page through results.
    57  `
    58  
    59  type listCmd struct {
    60  	filter     string
    61  	long       bool
    62  	limit      int
    63  	offset     string
    64  	byDate     bool
    65  	sortDesc   bool
    66  	out        io.Writer
    67  	all        bool
    68  	deleted    bool
    69  	deployed   bool
    70  	failed     bool
    71  	superseded bool
    72  	client     helm.Interface
    73  }
    74  
    75  func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
    76  	list := &listCmd{
    77  		out:    out,
    78  		client: client,
    79  	}
    80  	cmd := &cobra.Command{
    81  		Use:               "list [flags] [FILTER]",
    82  		Short:             "list releases",
    83  		Long:              listHelp,
    84  		Aliases:           []string{"ls"},
    85  		PersistentPreRunE: setupConnection,
    86  		RunE: func(cmd *cobra.Command, args []string) error {
    87  			if len(args) > 0 {
    88  				list.filter = strings.Join(args, " ")
    89  			}
    90  			if list.client == nil {
    91  				list.client = helm.NewClient(helm.Host(tillerHost))
    92  			}
    93  			return list.run()
    94  		},
    95  	}
    96  	f := cmd.Flags()
    97  	f.BoolVarP(&list.long, "long", "l", false, "output long listing format")
    98  	f.BoolVarP(&list.byDate, "date", "d", false, "sort by release date")
    99  	f.BoolVarP(&list.sortDesc, "reverse", "r", false, "reverse the sort order")
   100  	f.IntVarP(&list.limit, "max", "m", 256, "maximum number of releases to fetch")
   101  	f.StringVarP(&list.offset, "offset", "o", "", "the next release name in the list, used to offset from start value")
   102  	f.BoolVar(&list.all, "all", false, "show all releases, not just the ones marked DEPLOYED")
   103  	f.BoolVar(&list.deleted, "deleted", false, "show deleted releases")
   104  	f.BoolVar(&list.deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
   105  	f.BoolVar(&list.failed, "failed", false, "show failed releases")
   106  	// TODO: Do we want this as a feature of 'helm list'?
   107  	//f.BoolVar(&list.superseded, "history", true, "show historical releases")
   108  	return cmd
   109  }
   110  
   111  func (l *listCmd) run() error {
   112  	sortBy := services.ListSort_NAME
   113  	if l.byDate {
   114  		sortBy = services.ListSort_LAST_RELEASED
   115  	}
   116  
   117  	sortOrder := services.ListSort_ASC
   118  	if l.sortDesc {
   119  		sortOrder = services.ListSort_DESC
   120  	}
   121  
   122  	stats := l.statusCodes()
   123  
   124  	res, err := l.client.ListReleases(
   125  		helm.ReleaseListLimit(l.limit),
   126  		helm.ReleaseListOffset(l.offset),
   127  		helm.ReleaseListFilter(l.filter),
   128  		helm.ReleaseListSort(int32(sortBy)),
   129  		helm.ReleaseListOrder(int32(sortOrder)),
   130  		helm.ReleaseListStatuses(stats),
   131  	)
   132  
   133  	if err != nil {
   134  		return prettyError(err)
   135  	}
   136  
   137  	if len(res.Releases) == 0 {
   138  		return nil
   139  	}
   140  
   141  	if res.Next != "" {
   142  		fmt.Fprintf(l.out, "\tnext: %s", res.Next)
   143  	}
   144  
   145  	rels := res.Releases
   146  
   147  	if l.long {
   148  		fmt.Fprintln(l.out, formatList(rels))
   149  		return nil
   150  	}
   151  	for _, r := range rels {
   152  		fmt.Fprintln(l.out, r.Name)
   153  	}
   154  
   155  	return nil
   156  }
   157  
   158  // statusCodes gets the list of status codes that are to be included in the results.
   159  func (l *listCmd) statusCodes() []release.Status_Code {
   160  	if l.all {
   161  		return []release.Status_Code{
   162  			release.Status_UNKNOWN,
   163  			release.Status_DEPLOYED,
   164  			release.Status_DELETED,
   165  			// TODO: Should we return superseded records? These are records
   166  			// that were replaced by an upgrade.
   167  			//release.Status_SUPERSEDED,
   168  			release.Status_FAILED,
   169  		}
   170  	}
   171  	status := []release.Status_Code{}
   172  	if l.deployed {
   173  		status = append(status, release.Status_DEPLOYED)
   174  	}
   175  	if l.deleted {
   176  		status = append(status, release.Status_DELETED)
   177  	}
   178  	if l.failed {
   179  		status = append(status, release.Status_FAILED)
   180  	}
   181  	if l.superseded {
   182  		status = append(status, release.Status_SUPERSEDED)
   183  	}
   184  
   185  	// Default case.
   186  	if len(status) == 0 {
   187  		status = append(status, release.Status_DEPLOYED)
   188  	}
   189  	return status
   190  }
   191  
   192  func formatList(rels []*release.Release) string {
   193  	table := uitable.New()
   194  	table.MaxColWidth = 30
   195  	table.AddRow("NAME", "VERSION", "UPDATED", "STATUS", "CHART")
   196  	for _, r := range rels {
   197  		c := fmt.Sprintf("%s-%s", r.Chart.Metadata.Name, r.Chart.Metadata.Version)
   198  		t := timeconv.String(r.Info.LastDeployed)
   199  		s := r.Info.Status.Code.String()
   200  		v := r.Version
   201  		table.AddRow(r.Name, v, t, s, c)
   202  	}
   203  	return table.String()
   204  }