...

Source file src/helm.sh/helm/v3/pkg/action/list.go

Documentation: helm.sh/helm/v3/pkg/action

     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  	"path"
    21  	"regexp"
    22  
    23  	"k8s.io/apimachinery/pkg/labels"
    24  
    25  	"helm.sh/helm/v3/pkg/release"
    26  	"helm.sh/helm/v3/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 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 progress)
    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  // It returns no more than one revision of every release in one specific, or in
   102  // all, namespaces.
   103  // To list all the revisions of a specific release, see the History action.
   104  type List struct {
   105  	cfg *Configuration
   106  
   107  	// All ignores the limit/offset
   108  	All bool
   109  	// AllNamespaces searches across namespaces
   110  	AllNamespaces bool
   111  	// Sort indicates the sort to use
   112  	//
   113  	// see pkg/releaseutil for several useful sorters
   114  	Sort Sorter
   115  	// Overrides the default lexicographic sorting
   116  	ByDate      bool
   117  	SortReverse bool
   118  	// StateMask accepts a bitmask of states for items to show.
   119  	// The default is ListDeployed
   120  	StateMask ListStates
   121  	// Limit is the number of items to return per Run()
   122  	Limit int
   123  	// Offset is the starting index for the Run() call
   124  	Offset int
   125  	// Filter is a filter that is applied to the results
   126  	Filter       string
   127  	Short        bool
   128  	NoHeaders    bool
   129  	TimeFormat   string
   130  	Uninstalled  bool
   131  	Superseded   bool
   132  	Uninstalling bool
   133  	Deployed     bool
   134  	Failed       bool
   135  	Pending      bool
   136  	Selector     string
   137  }
   138  
   139  // NewList constructs a new *List
   140  func NewList(cfg *Configuration) *List {
   141  	return &List{
   142  		StateMask: ListDeployed | ListFailed,
   143  		cfg:       cfg,
   144  	}
   145  }
   146  
   147  // Run executes the list command, returning a set of matches.
   148  func (l *List) Run() ([]*release.Release, error) {
   149  	if err := l.cfg.KubeClient.IsReachable(); err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	var filter *regexp.Regexp
   154  	if l.Filter != "" {
   155  		var err error
   156  		filter, err = regexp.Compile(l.Filter)
   157  		if err != nil {
   158  			return nil, err
   159  		}
   160  	}
   161  
   162  	results, err := l.cfg.Releases.List(func(rel *release.Release) bool {
   163  		// Skip anything that doesn't match the filter.
   164  		if filter != nil && !filter.MatchString(rel.Name) {
   165  			return false
   166  		}
   167  
   168  		return true
   169  	})
   170  
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	if results == nil {
   176  		return results, nil
   177  	}
   178  
   179  	// by definition, superseded releases are never shown if
   180  	// only the latest releases are returned. so if requested statemask
   181  	// is _only_ ListSuperseded, skip the latest release filter
   182  	if l.StateMask != ListSuperseded {
   183  		results = filterLatestReleases(results)
   184  	}
   185  
   186  	// State mask application must occur after filtering to
   187  	// latest releases, otherwise outdated entries can be returned
   188  	results = l.filterStateMask(results)
   189  
   190  	// Skip anything that doesn't match the selector
   191  	selectorObj, err := labels.Parse(l.Selector)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  	results = l.filterSelector(results, selectorObj)
   196  
   197  	// Unfortunately, we have to sort before truncating, which can incur substantial overhead
   198  	l.sort(results)
   199  
   200  	// Guard on offset
   201  	if l.Offset >= len(results) {
   202  		return []*release.Release{}, nil
   203  	}
   204  
   205  	// Calculate the limit and offset, and then truncate results if necessary.
   206  	limit := len(results)
   207  	if l.Limit > 0 && l.Limit < limit {
   208  		limit = l.Limit
   209  	}
   210  	last := l.Offset + limit
   211  	if l := len(results); l < last {
   212  		last = l
   213  	}
   214  	results = results[l.Offset:last]
   215  
   216  	return results, err
   217  }
   218  
   219  // sort is an in-place sort where order is based on the value of a.Sort
   220  func (l *List) sort(rels []*release.Release) {
   221  	if l.SortReverse {
   222  		l.Sort = ByNameDesc
   223  	}
   224  
   225  	if l.ByDate {
   226  		l.Sort = ByDateDesc
   227  		if l.SortReverse {
   228  			l.Sort = ByDateAsc
   229  		}
   230  	}
   231  
   232  	switch l.Sort {
   233  	case ByDateDesc:
   234  		releaseutil.SortByDate(rels)
   235  	case ByDateAsc:
   236  		releaseutil.Reverse(rels, releaseutil.SortByDate)
   237  	case ByNameDesc:
   238  		releaseutil.Reverse(rels, releaseutil.SortByName)
   239  	default:
   240  		releaseutil.SortByName(rels)
   241  	}
   242  }
   243  
   244  // filterLatestReleases returns a list scrubbed of old releases.
   245  func filterLatestReleases(releases []*release.Release) []*release.Release {
   246  	latestReleases := make(map[string]*release.Release)
   247  
   248  	for _, rls := range releases {
   249  		name, namespace := rls.Name, rls.Namespace
   250  		key := path.Join(namespace, name)
   251  		if latestRelease, exists := latestReleases[key]; exists && latestRelease.Version > rls.Version {
   252  			continue
   253  		}
   254  		latestReleases[key] = rls
   255  	}
   256  
   257  	var list = make([]*release.Release, 0, len(latestReleases))
   258  	for _, rls := range latestReleases {
   259  		list = append(list, rls)
   260  	}
   261  	return list
   262  }
   263  
   264  func (l *List) filterStateMask(releases []*release.Release) []*release.Release {
   265  	desiredStateReleases := make([]*release.Release, 0)
   266  
   267  	for _, rls := range releases {
   268  		currentStatus := l.StateMask.FromName(rls.Info.Status.String())
   269  		mask := l.StateMask & currentStatus
   270  		if mask == 0 {
   271  			continue
   272  		}
   273  		desiredStateReleases = append(desiredStateReleases, rls)
   274  	}
   275  
   276  	return desiredStateReleases
   277  }
   278  
   279  func (l *List) filterSelector(releases []*release.Release, selector labels.Selector) []*release.Release {
   280  	desiredStateReleases := make([]*release.Release, 0)
   281  
   282  	for _, rls := range releases {
   283  		if selector.Matches(labels.Set(rls.Labels)) {
   284  			desiredStateReleases = append(desiredStateReleases, rls)
   285  		}
   286  	}
   287  
   288  	return desiredStateReleases
   289  }
   290  
   291  // SetStateMask calculates the state mask based on parameters.
   292  func (l *List) SetStateMask() {
   293  	if l.All {
   294  		l.StateMask = ListAll
   295  		return
   296  	}
   297  
   298  	state := ListStates(0)
   299  	if l.Deployed {
   300  		state |= ListDeployed
   301  	}
   302  	if l.Uninstalled {
   303  		state |= ListUninstalled
   304  	}
   305  	if l.Uninstalling {
   306  		state |= ListUninstalling
   307  	}
   308  	if l.Pending {
   309  		state |= ListPendingInstall | ListPendingRollback | ListPendingUpgrade
   310  	}
   311  	if l.Failed {
   312  		state |= ListFailed
   313  	}
   314  	if l.Superseded {
   315  		state |= ListSuperseded
   316  	}
   317  
   318  	// Apply a default
   319  	if state == 0 {
   320  		state = ListDeployed | ListFailed
   321  	}
   322  
   323  	l.StateMask = state
   324  }
   325  

View as plain text