mirror of https://github.com/helm/helm
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
274 lines
7.7 KiB
274 lines
7.7 KiB
/*
|
|
Copyright The Helm Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package action
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
|
|
"github.com/gosuri/uitable"
|
|
"github.com/spf13/pflag"
|
|
|
|
"k8s.io/helm/pkg/release"
|
|
"k8s.io/helm/pkg/releaseutil"
|
|
)
|
|
|
|
// ListStates represents zero or more status codes that a list item may have set
|
|
//
|
|
// Because this is used as a bitmask filter, more than one one bit can be flipped
|
|
// in the ListStates.
|
|
type ListStates uint
|
|
|
|
const (
|
|
// ListDeployed filters on status "deployed"
|
|
ListDeployed ListStates = 1 << iota
|
|
// ListUninstalled filters on status "uninstalled"
|
|
ListUninstalled
|
|
// ListUninstalling filters on status "uninstalling" (uninstall in progress)
|
|
ListUninstalling
|
|
// ListPendingInstall filters on status "pending" (deployment in progress)
|
|
ListPendingInstall
|
|
// ListPendingUpgrade filters on status "pending_upgrade" (upgrade in progress)
|
|
ListPendingUpgrade
|
|
// ListPendingRollback filters on status "pending_rollback" (rollback in progres)
|
|
ListPendingRollback
|
|
// ListSuperseded filters on status "superseded" (historical release version that is no longer deployed)
|
|
ListSuperseded
|
|
// ListFailed filters on status "failed" (release version not deployed because of error)
|
|
ListFailed
|
|
// ListUnknown filters on an unknown status
|
|
ListUnknown
|
|
)
|
|
|
|
// FromName takes a state name and returns a ListStates representation.
|
|
//
|
|
// Currently, there are only names for individual flipped bits, so the returned
|
|
// ListStates will only match one of the constants. However, it is possible that
|
|
// this behavior could change in the future.
|
|
func (s ListStates) FromName(str string) ListStates {
|
|
switch str {
|
|
case "deployed":
|
|
return ListDeployed
|
|
case "uninstalled":
|
|
return ListUninstalled
|
|
case "superseded":
|
|
return ListSuperseded
|
|
case "failed":
|
|
return ListFailed
|
|
case "uninstalling":
|
|
return ListUninstalling
|
|
case "pending-install":
|
|
return ListPendingInstall
|
|
case "pending-upgrade":
|
|
return ListPendingUpgrade
|
|
case "pending-rollback":
|
|
return ListPendingRollback
|
|
}
|
|
return ListUnknown
|
|
}
|
|
|
|
// ListAll is a convenience for enabling all list filters
|
|
const ListAll = ListDeployed | ListUninstalled | ListUninstalling | ListPendingInstall | ListPendingRollback | ListPendingUpgrade | ListSuperseded | ListFailed
|
|
|
|
// Sorter is a top-level sort
|
|
type Sorter uint
|
|
|
|
const (
|
|
// ByDate sorts by date
|
|
ByDate Sorter = iota
|
|
// ByNameAsc sorts by ascending lexicographic order
|
|
ByNameAsc
|
|
// ByNameDesc sorts by descending lexicographic order
|
|
ByNameDesc
|
|
)
|
|
|
|
// List is the action for listing releases.
|
|
//
|
|
// It provides, for example, the implementation of 'helm list'.
|
|
type List struct {
|
|
cfg *Configuration
|
|
|
|
// All ignores the limit/offset
|
|
All bool
|
|
// AllNamespaces searches across namespaces
|
|
AllNamespaces bool
|
|
// Sort indicates the sort to use
|
|
//
|
|
// see pkg/releaseutil for several useful sorters
|
|
Sort Sorter
|
|
// StateMask accepts a bitmask of states for items to show.
|
|
// The default is ListDeployed
|
|
StateMask ListStates
|
|
// Limit is the number of items to return per Run()
|
|
Limit int
|
|
// Offset is the starting index for the Run() call
|
|
Offset int
|
|
// Filter is a filter that is applied to the results
|
|
Filter string
|
|
Short bool
|
|
ByDate bool
|
|
SortDesc bool
|
|
Uninstalled bool
|
|
Superseded bool
|
|
Uninstalling bool
|
|
Deployed bool
|
|
Failed bool
|
|
Pending bool
|
|
}
|
|
|
|
// NewList constructs a new *List
|
|
func NewList(cfg *Configuration) *List {
|
|
return &List{
|
|
StateMask: ListDeployed | ListFailed,
|
|
cfg: cfg,
|
|
}
|
|
}
|
|
|
|
func (l *List) AddFlags(f *pflag.FlagSet) {
|
|
f.BoolVarP(&l.Short, "short", "q", false, "output short (quiet) listing format")
|
|
f.BoolVarP(&l.ByDate, "date", "d", false, "sort by release date")
|
|
f.BoolVarP(&l.SortDesc, "reverse", "r", false, "reverse the sort order")
|
|
f.BoolVarP(&l.All, "all", "a", false, "show all releases, not just the ones marked deployed")
|
|
f.BoolVar(&l.Uninstalled, "uninstalled", false, "show uninstalled releases")
|
|
f.BoolVar(&l.Superseded, "superseded", false, "show superseded releases")
|
|
f.BoolVar(&l.Uninstalling, "uninstalling", false, "show releases that are currently being uninstalled")
|
|
f.BoolVar(&l.Deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
|
|
f.BoolVar(&l.Failed, "failed", false, "show failed releases")
|
|
f.BoolVar(&l.Pending, "pending", false, "show pending releases")
|
|
f.BoolVar(&l.AllNamespaces, "all-namespaces", false, "list releases across all namespaces")
|
|
f.IntVarP(&l.Limit, "max", "m", 256, "maximum number of releases to fetch")
|
|
f.IntVarP(&l.Offset, "offset", "o", 0, "next release name in the list, used to offset from start value")
|
|
f.StringVarP(&l.Filter, "filter", "f", "", "a regular expression (Perl compatible). Any releases that match the expression will be included in the results")
|
|
}
|
|
|
|
func (l *List) SetConfiguration(cfg *Configuration) {
|
|
l.cfg = cfg
|
|
}
|
|
|
|
// Run executes the list command, returning a set of matches.
|
|
func (l *List) Run() ([]*release.Release, error) {
|
|
var filter *regexp.Regexp
|
|
if l.Filter != "" {
|
|
var err error
|
|
filter, err = regexp.Compile(l.Filter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
results, err := l.cfg.Releases.List(func(rel *release.Release) bool {
|
|
// Skip anything that the mask doesn't cover
|
|
currentStatus := l.StateMask.FromName(rel.Info.Status.String())
|
|
if l.StateMask¤tStatus == 0 {
|
|
return false
|
|
}
|
|
|
|
// Skip anything that doesn't match the filter.
|
|
if filter != nil && !filter.MatchString(rel.Name) {
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
|
|
if results == nil {
|
|
return results, nil
|
|
}
|
|
|
|
// Unfortunately, we have to sort before truncating, which can incur substantial overhead
|
|
l.sort(results)
|
|
|
|
// Guard on offset
|
|
if l.Offset >= len(results) {
|
|
return []*release.Release{}, nil
|
|
}
|
|
|
|
// Calculate the limit and offset, and then truncate results if necessary.
|
|
limit := len(results)
|
|
if l.Limit > 0 && l.Limit < limit {
|
|
limit = l.Limit
|
|
}
|
|
last := l.Offset + limit
|
|
if l := len(results); l < last {
|
|
last = l
|
|
}
|
|
results = results[l.Offset:last]
|
|
|
|
return results, err
|
|
}
|
|
|
|
// sort is an in-place sort where order is based on the value of a.Sort
|
|
func (l *List) sort(rels []*release.Release) {
|
|
switch l.Sort {
|
|
case ByDate:
|
|
releaseutil.SortByDate(rels)
|
|
case ByNameDesc:
|
|
releaseutil.Reverse(rels, releaseutil.SortByName)
|
|
default:
|
|
releaseutil.SortByName(rels)
|
|
}
|
|
}
|
|
|
|
// setStateMask calculates the state mask based on parameters.
|
|
func (l *List) SetStateMask() {
|
|
if l.All {
|
|
l.StateMask = ListAll
|
|
return
|
|
}
|
|
|
|
state := ListStates(0)
|
|
if l.Deployed {
|
|
state |= ListDeployed
|
|
}
|
|
if l.Uninstalled {
|
|
state |= ListUninstalled
|
|
}
|
|
if l.Uninstalling {
|
|
state |= ListUninstalling
|
|
}
|
|
if l.Pending {
|
|
state |= ListPendingInstall | ListPendingRollback | ListPendingUpgrade
|
|
}
|
|
if l.Failed {
|
|
state |= ListFailed
|
|
}
|
|
|
|
// Apply a default
|
|
if state == 0 {
|
|
state = ListDeployed | ListFailed
|
|
}
|
|
|
|
l.StateMask = state
|
|
}
|
|
|
|
func FormatList(rels []*release.Release) string {
|
|
table := uitable.New()
|
|
table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "NAMESPACE")
|
|
for _, r := range rels {
|
|
md := r.Chart.Metadata
|
|
c := fmt.Sprintf("%s-%s", md.Name, md.Version)
|
|
t := "-"
|
|
if tspb := r.Info.LastDeployed; !tspb.IsZero() {
|
|
t = tspb.String()
|
|
}
|
|
s := r.Info.Status.String()
|
|
v := r.Version
|
|
n := r.Namespace
|
|
table.AddRow(r.Name, v, t, s, c, n)
|
|
}
|
|
return table.String()
|
|
}
|