...
1
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
30
31
32
33 type ListStates uint
34
35 const (
36
37 ListDeployed ListStates = 1 << iota
38
39 ListUninstalled
40
41 ListUninstalling
42
43 ListPendingInstall
44
45 ListPendingUpgrade
46
47 ListPendingRollback
48
49 ListSuperseded
50
51 ListFailed
52
53 ListUnknown
54 )
55
56
57
58
59
60
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
84 const ListAll = ListDeployed | ListUninstalled | ListUninstalling | ListPendingInstall | ListPendingRollback | ListPendingUpgrade | ListSuperseded | ListFailed
85
86
87 type Sorter uint
88
89 const (
90
91 ByNameDesc Sorter = iota + 1
92
93 ByDateAsc
94
95 ByDateDesc
96 )
97
98
99
100
101
102
103
104 type List struct {
105 cfg *Configuration
106
107
108 All bool
109
110 AllNamespaces bool
111
112
113
114 Sort Sorter
115
116 ByDate bool
117 SortReverse bool
118
119
120 StateMask ListStates
121
122 Limit int
123
124 Offset int
125
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
140 func NewList(cfg *Configuration) *List {
141 return &List{
142 StateMask: ListDeployed | ListFailed,
143 cfg: cfg,
144 }
145 }
146
147
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
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
180
181
182 if l.StateMask != ListSuperseded {
183 results = filterLatestReleases(results)
184 }
185
186
187
188 results = l.filterStateMask(results)
189
190
191 selectorObj, err := labels.Parse(l.Selector)
192 if err != nil {
193 return nil, err
194 }
195 results = l.filterSelector(results, selectorObj)
196
197
198 l.sort(results)
199
200
201 if l.Offset >= len(results) {
202 return []*release.Release{}, nil
203 }
204
205
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
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
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
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
319 if state == 0 {
320 state = ListDeployed | ListFailed
321 }
322
323 l.StateMask = state
324 }
325
View as plain text