github.com/aaronmell/helm@v3.0.0-beta.2+incompatible/pkg/action/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 action
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  
    23  	"github.com/gosuri/uitable"
    24  
    25  	"helm.sh/helm/pkg/release"
    26  	"helm.sh/helm/pkg/releaseutil"
    27  )
    28  
    29  // ListStates represents zero or more status codes that a list item may have set
    30  //
    31  // Because this is used as a bitmask filter, more than one one bit can be flipped
    32  // in the ListStates.
    33  type ListStates uint
    34  
    35  const (
    36  	// ListDeployed filters on status "deployed"
    37  	ListDeployed ListStates = 1 << iota
    38  	// ListUninstalled filters on status "uninstalled"
    39  	ListUninstalled
    40  	// ListUninstalling filters on status "uninstalling" (uninstall in progress)
    41  	ListUninstalling
    42  	// ListPendingInstall filters on status "pending" (deployment in progress)
    43  	ListPendingInstall
    44  	// ListPendingUpgrade filters on status "pending_upgrade" (upgrade in progress)
    45  	ListPendingUpgrade
    46  	// ListPendingRollback filters on status "pending_rollback" (rollback in progres)
    47  	ListPendingRollback
    48  	// ListSuperseded filters on status "superseded" (historical release version that is no longer deployed)
    49  	ListSuperseded
    50  	// ListFailed filters on status "failed" (release version not deployed because of error)
    51  	ListFailed
    52  	// ListUnknown filters on an unknown status
    53  	ListUnknown
    54  )
    55  
    56  // FromName takes a state name and returns a ListStates representation.
    57  //
    58  // Currently, there are only names for individual flipped bits, so the returned
    59  // ListStates will only match one of the constants. However, it is possible that
    60  // this behavior could change in the future.
    61  func (s ListStates) FromName(str string) ListStates {
    62  	switch str {
    63  	case "deployed":
    64  		return ListDeployed
    65  	case "uninstalled":
    66  		return ListUninstalled
    67  	case "superseded":
    68  		return ListSuperseded
    69  	case "failed":
    70  		return ListFailed
    71  	case "uninstalling":
    72  		return ListUninstalling
    73  	case "pending-install":
    74  		return ListPendingInstall
    75  	case "pending-upgrade":
    76  		return ListPendingUpgrade
    77  	case "pending-rollback":
    78  		return ListPendingRollback
    79  	}
    80  	return ListUnknown
    81  }
    82  
    83  // ListAll is a convenience for enabling all list filters
    84  const ListAll = ListDeployed | ListUninstalled | ListUninstalling | ListPendingInstall | ListPendingRollback | ListPendingUpgrade | ListSuperseded | ListFailed
    85  
    86  // Sorter is a top-level sort
    87  type Sorter uint
    88  
    89  const (
    90  	// ByNameDesc sorts by descending lexicographic order
    91  	ByNameDesc Sorter = iota + 1
    92  	// ByDateAsc sorts by ascending dates (oldest updated release first)
    93  	ByDateAsc
    94  	// ByDateDesc sorts by descending dates (latest updated release first)
    95  	ByDateDesc
    96  )
    97  
    98  // List is the action for listing releases.
    99  //
   100  // It provides, for example, the implementation of 'helm list'.
   101  type List struct {
   102  	cfg *Configuration
   103  
   104  	// All ignores the limit/offset
   105  	All bool
   106  	// AllNamespaces searches across namespaces
   107  	AllNamespaces bool
   108  	// Sort indicates the sort to use
   109  	//
   110  	// see pkg/releaseutil for several useful sorters
   111  	Sort Sorter
   112  	// Overrides the default lexicographic sorting
   113  	ByDate      bool
   114  	SortReverse bool
   115  	// StateMask accepts a bitmask of states for items to show.
   116  	// The default is ListDeployed
   117  	StateMask ListStates
   118  	// Limit is the number of items to return per Run()
   119  	Limit int
   120  	// Offset is the starting index for the Run() call
   121  	Offset int
   122  	// Filter is a filter that is applied to the results
   123  	Filter       string
   124  	Short        bool
   125  	Uninstalled  bool
   126  	Superseded   bool
   127  	Uninstalling bool
   128  	Deployed     bool
   129  	Failed       bool
   130  	Pending      bool
   131  }
   132  
   133  // NewList constructs a new *List
   134  func NewList(cfg *Configuration) *List {
   135  	return &List{
   136  		StateMask: ListDeployed | ListFailed,
   137  		cfg:       cfg,
   138  	}
   139  }
   140  
   141  // Run executes the list command, returning a set of matches.
   142  func (l *List) Run() ([]*release.Release, error) {
   143  	var filter *regexp.Regexp
   144  	if l.Filter != "" {
   145  		var err error
   146  		filter, err = regexp.Compile(l.Filter)
   147  		if err != nil {
   148  			return nil, err
   149  		}
   150  	}
   151  
   152  	results, err := l.cfg.Releases.List(func(rel *release.Release) bool {
   153  		// Skip anything that the mask doesn't cover
   154  		currentStatus := l.StateMask.FromName(rel.Info.Status.String())
   155  		if l.StateMask&currentStatus == 0 {
   156  			return false
   157  		}
   158  
   159  		// Skip anything that doesn't match the filter.
   160  		if filter != nil && !filter.MatchString(rel.Name) {
   161  			return false
   162  		}
   163  		return true
   164  	})
   165  
   166  	if results == nil {
   167  		return results, nil
   168  	}
   169  
   170  	// Unfortunately, we have to sort before truncating, which can incur substantial overhead
   171  	l.sort(results)
   172  
   173  	// Guard on offset
   174  	if l.Offset >= len(results) {
   175  		return []*release.Release{}, nil
   176  	}
   177  
   178  	// Calculate the limit and offset, and then truncate results if necessary.
   179  	limit := len(results)
   180  	if l.Limit > 0 && l.Limit < limit {
   181  		limit = l.Limit
   182  	}
   183  	last := l.Offset + limit
   184  	if l := len(results); l < last {
   185  		last = l
   186  	}
   187  	results = results[l.Offset:last]
   188  
   189  	return results, err
   190  }
   191  
   192  // sort is an in-place sort where order is based on the value of a.Sort
   193  func (l *List) sort(rels []*release.Release) {
   194  	if l.SortReverse {
   195  		l.Sort = ByNameDesc
   196  	}
   197  
   198  	if l.ByDate {
   199  		l.Sort = ByDateDesc
   200  		if l.SortReverse {
   201  			l.Sort = ByDateAsc
   202  		}
   203  	}
   204  
   205  	switch l.Sort {
   206  	case ByDateDesc:
   207  		releaseutil.SortByDate(rels)
   208  	case ByDateAsc:
   209  		releaseutil.Reverse(rels, releaseutil.SortByDate)
   210  	case ByNameDesc:
   211  		releaseutil.Reverse(rels, releaseutil.SortByName)
   212  	default:
   213  		releaseutil.SortByName(rels)
   214  	}
   215  }
   216  
   217  // setStateMask calculates the state mask based on parameters.
   218  func (l *List) SetStateMask() {
   219  	if l.All {
   220  		l.StateMask = ListAll
   221  		return
   222  	}
   223  
   224  	state := ListStates(0)
   225  	if l.Deployed {
   226  		state |= ListDeployed
   227  	}
   228  	if l.Uninstalled {
   229  		state |= ListUninstalled
   230  	}
   231  	if l.Uninstalling {
   232  		state |= ListUninstalling
   233  	}
   234  	if l.Pending {
   235  		state |= ListPendingInstall | ListPendingRollback | ListPendingUpgrade
   236  	}
   237  	if l.Failed {
   238  		state |= ListFailed
   239  	}
   240  	if l.Superseded {
   241  		state |= ListSuperseded
   242  	}
   243  
   244  	// Apply a default
   245  	if state == 0 {
   246  		state = ListDeployed | ListFailed
   247  	}
   248  
   249  	l.StateMask = state
   250  }
   251  
   252  func FormatList(rels []*release.Release) string {
   253  	table := uitable.New()
   254  	table.AddRow("NAME", "NAMESPACE", "REVISION", "UPDATED", "STATUS", "CHART")
   255  	for _, r := range rels {
   256  		md := r.Chart.Metadata
   257  		c := fmt.Sprintf("%s-%s", md.Name, md.Version)
   258  		t := "-"
   259  		if tspb := r.Info.LastDeployed; !tspb.IsZero() {
   260  			t = tspb.String()
   261  		}
   262  		s := r.Info.Status.String()
   263  		v := r.Version
   264  		n := r.Namespace
   265  		table.AddRow(r.Name, n, v, t, s, c)
   266  	}
   267  	return table.String()
   268  }