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