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¤tStatus == 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 }