github.com/x-helm/helm@v3.0.0-beta.3+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  	if err := l.cfg.KubeClient.IsReachable(); err != nil {
   144  		return nil, err
   145  	}
   146  
   147  	var filter *regexp.Regexp
   148  	if l.Filter != "" {
   149  		var err error
   150  		filter, err = regexp.Compile(l.Filter)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  	}
   155  
   156  	results, err := l.cfg.Releases.List(func(rel *release.Release) bool {
   157  		// Skip anything that the mask doesn't cover
   158  		currentStatus := l.StateMask.FromName(rel.Info.Status.String())
   159  		if l.StateMask&currentStatus == 0 {
   160  			return false
   161  		}
   162  
   163  		// Skip anything that doesn't match the filter.
   164  		if filter != nil && !filter.MatchString(rel.Name) {
   165  			return false
   166  		}
   167  		return true
   168  	})
   169  
   170  	if results == nil {
   171  		return results, nil
   172  	}
   173  
   174  	// Unfortunately, we have to sort before truncating, which can incur substantial overhead
   175  	l.sort(results)
   176  
   177  	// Guard on offset
   178  	if l.Offset >= len(results) {
   179  		return []*release.Release{}, nil
   180  	}
   181  
   182  	// Calculate the limit and offset, and then truncate results if necessary.
   183  	limit := len(results)
   184  	if l.Limit > 0 && l.Limit < limit {
   185  		limit = l.Limit
   186  	}
   187  	last := l.Offset + limit
   188  	if l := len(results); l < last {
   189  		last = l
   190  	}
   191  	results = results[l.Offset:last]
   192  
   193  	return results, err
   194  }
   195  
   196  // sort is an in-place sort where order is based on the value of a.Sort
   197  func (l *List) sort(rels []*release.Release) {
   198  	if l.SortReverse {
   199  		l.Sort = ByNameDesc
   200  	}
   201  
   202  	if l.ByDate {
   203  		l.Sort = ByDateDesc
   204  		if l.SortReverse {
   205  			l.Sort = ByDateAsc
   206  		}
   207  	}
   208  
   209  	switch l.Sort {
   210  	case ByDateDesc:
   211  		releaseutil.SortByDate(rels)
   212  	case ByDateAsc:
   213  		releaseutil.Reverse(rels, releaseutil.SortByDate)
   214  	case ByNameDesc:
   215  		releaseutil.Reverse(rels, releaseutil.SortByName)
   216  	default:
   217  		releaseutil.SortByName(rels)
   218  	}
   219  }
   220  
   221  // setStateMask calculates the state mask based on parameters.
   222  func (l *List) SetStateMask() {
   223  	if l.All {
   224  		l.StateMask = ListAll
   225  		return
   226  	}
   227  
   228  	state := ListStates(0)
   229  	if l.Deployed {
   230  		state |= ListDeployed
   231  	}
   232  	if l.Uninstalled {
   233  		state |= ListUninstalled
   234  	}
   235  	if l.Uninstalling {
   236  		state |= ListUninstalling
   237  	}
   238  	if l.Pending {
   239  		state |= ListPendingInstall | ListPendingRollback | ListPendingUpgrade
   240  	}
   241  	if l.Failed {
   242  		state |= ListFailed
   243  	}
   244  	if l.Superseded {
   245  		state |= ListSuperseded
   246  	}
   247  
   248  	// Apply a default
   249  	if state == 0 {
   250  		state = ListDeployed | ListFailed
   251  	}
   252  
   253  	l.StateMask = state
   254  }
   255  
   256  func FormatList(rels []*release.Release) string {
   257  	table := uitable.New()
   258  	table.AddRow("NAME", "NAMESPACE", "REVISION", "UPDATED", "STATUS", "CHART")
   259  	for _, r := range rels {
   260  		md := r.Chart.Metadata
   261  		c := fmt.Sprintf("%s-%s", md.Name, md.Version)
   262  		t := "-"
   263  		if tspb := r.Info.LastDeployed; !tspb.IsZero() {
   264  			t = tspb.String()
   265  		}
   266  		s := r.Info.Status.String()
   267  		v := r.Version
   268  		n := r.Namespace
   269  		table.AddRow(r.Name, n, v, t, s, c)
   270  	}
   271  	return table.String()
   272  }