github.com/wangchanggan/helm@v0.0.0-20211020154240-11b1b7d5406d/cmd/helm/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 main 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "io" 23 "strings" 24 25 "github.com/ghodss/yaml" 26 "github.com/gosuri/uitable" 27 "github.com/spf13/cobra" 28 29 "k8s.io/helm/pkg/helm" 30 "k8s.io/helm/pkg/proto/hapi/release" 31 "k8s.io/helm/pkg/proto/hapi/services" 32 "k8s.io/helm/pkg/timeconv" 33 ) 34 35 var listHelp = ` 36 This command lists all of the releases. 37 38 By default, it lists only releases that are deployed or failed. Flags like 39 '--deleted' and '--all' will alter this behavior. Such flags can be combined: 40 '--deleted --failed'. 41 42 By default, items are sorted alphabetically. Use the '-d' flag to sort by 43 release date. 44 45 If an argument is provided, it will be treated as a filter. Filters are 46 regular expressions (Perl compatible) that are applied to the list of releases. 47 Only items that match the filter will be returned. 48 49 $ helm list 'ara[a-z]+' 50 NAME UPDATED CHART 51 maudlin-arachnid Mon May 9 16:07:08 2016 alpine-0.1.0 52 53 If no results are found, 'helm list' will exit 0, but with no output (or in 54 the case of no '-q' flag, only headers). 55 56 By default, up to 256 items may be returned. To limit this, use the '--max' flag. 57 Setting '--max' to 0 will not return all results. Rather, it will return the 58 server's default, which may be much higher than 256. Pairing the '--max' 59 flag with the '--offset' flag allows you to page through results. 60 ` 61 62 type listCmd struct { 63 filter string 64 short bool 65 limit int 66 offset string 67 byDate bool 68 sortDesc bool 69 out io.Writer 70 all bool 71 deleted bool 72 deleting bool 73 deployed bool 74 failed bool 75 namespace string 76 superseded bool 77 pending bool 78 client helm.Interface 79 colWidth uint 80 output string 81 byChartName bool 82 } 83 84 type listResult struct { 85 Next string 86 Releases []listRelease 87 } 88 89 type listRelease struct { 90 Name string 91 Revision int32 92 Updated string 93 Status string 94 Chart string 95 AppVersion string 96 Namespace string 97 } 98 99 func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { 100 list := &listCmd{ 101 out: out, 102 client: client, 103 } 104 105 cmd := &cobra.Command{ 106 Use: "list [flags] [FILTER]", 107 Short: "List releases", 108 Long: listHelp, 109 Aliases: []string{"ls"}, 110 PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() }, 111 RunE: func(cmd *cobra.Command, args []string) error { 112 if len(args) > 0 { 113 list.filter = strings.Join(args, " ") 114 } 115 if list.client == nil { 116 list.client = newClient() 117 } 118 return list.run() 119 }, 120 } 121 122 f := cmd.Flags() 123 settings.AddFlagsTLS(f) 124 f.BoolVarP(&list.short, "short", "q", false, "Output short (quiet) listing format") 125 f.BoolVarP(&list.byDate, "date", "d", false, "Sort by release date") 126 f.BoolVarP(&list.sortDesc, "reverse", "r", false, "Reverse the sort order") 127 f.IntVarP(&list.limit, "max", "m", 256, "Maximum number of releases to fetch") 128 f.StringVarP(&list.offset, "offset", "o", "", "Next release name in the list, used to offset from start value") 129 f.BoolVarP(&list.all, "all", "a", false, "Show all releases, not just the ones marked DEPLOYED") 130 f.BoolVar(&list.deleted, "deleted", false, "Show deleted releases") 131 f.BoolVar(&list.deleting, "deleting", false, "Show releases that are currently being deleted") 132 f.BoolVar(&list.deployed, "deployed", false, "Show deployed releases. If no other is specified, this will be automatically enabled") 133 f.BoolVar(&list.failed, "failed", false, "Show failed releases") 134 f.BoolVar(&list.pending, "pending", false, "Show pending releases") 135 f.StringVar(&list.namespace, "namespace", "", "Show releases within a specific namespace") 136 f.UintVar(&list.colWidth, "col-width", 60, "Specifies the max column width of output") 137 f.StringVar(&list.output, "output", "", "Output the specified format (json or yaml)") 138 f.BoolVarP(&list.byChartName, "chart-name", "c", false, "Sort by chart name") 139 140 // TODO: Do we want this as a feature of 'helm list'? 141 //f.BoolVar(&list.superseded, "history", true, "show historical releases") 142 143 // set defaults from environment 144 settings.InitTLS(f) 145 146 return cmd 147 } 148 149 func (l *listCmd) run() error { 150 // 状态和查询参数设置, 主要目的是限制每次查询返回结果的数量,然后规定以哪种方式对结果排序 151 // 同时还支持按照状态来返回在询的结果,设置根据命名空间查询 152 sortBy := services.ListSort_NAME 153 if l.byDate { 154 sortBy = services.ListSort_LAST_RELEASED 155 } 156 if l.byChartName { 157 sortBy = services.ListSort_CHART_NAME 158 } 159 160 sortOrder := services.ListSort_ASC 161 if l.sortDesc { 162 sortOrder = services.ListSort_DESC 163 } 164 165 stats := l.statusCodes() 166 167 res, err := l.client.ListReleases( 168 helm.ReleaseListLimit(l.limit), 169 helm.ReleaseListOffset(l.offset), 170 helm.ReleaseListFilter(l.filter), 171 helm.ReleaseListSort(int32(sortBy)), 172 helm.ReleaseListOrder(int32(sortOrder)), 173 helm.ReleaseListStatuses(stats), 174 helm.ReleaseListNamespace(l.namespace), 175 ) 176 177 if err != nil { 178 return prettyError(err) 179 } 180 if res == nil { 181 return nil 182 } 183 184 rels := filterList(res.GetReleases()) 185 186 result := getListResult(rels, res.Next) 187 188 output, err := formatResult(l.output, l.short, result, l.colWidth) 189 190 if err != nil { 191 return prettyError(err) 192 } 193 194 fmt.Fprintln(l.out, output) 195 return nil 196 } 197 198 // filterList returns a list scrubbed of old releases. 199 func filterList(rels []*release.Release) []*release.Release { 200 idx := map[string]int32{} 201 202 for _, r := range rels { 203 name, version := r.GetName(), r.GetVersion() 204 if max, ok := idx[name]; ok { 205 // check if we have a greater version already 206 if max > version { 207 continue 208 } 209 } 210 idx[name] = version 211 } 212 213 uniq := make([]*release.Release, 0, len(idx)) 214 for _, r := range rels { 215 if idx[r.GetName()] == r.GetVersion() { 216 uniq = append(uniq, r) 217 } 218 } 219 return uniq 220 } 221 222 // statusCodes gets the list of status codes that are to be included in the results. 223 func (l *listCmd) statusCodes() []release.Status_Code { 224 if l.all { 225 return []release.Status_Code{ 226 release.Status_UNKNOWN, 227 release.Status_DEPLOYED, 228 release.Status_DELETED, 229 release.Status_DELETING, 230 release.Status_FAILED, 231 release.Status_PENDING_INSTALL, 232 release.Status_PENDING_UPGRADE, 233 release.Status_PENDING_ROLLBACK, 234 } 235 } 236 status := []release.Status_Code{} 237 if l.deployed { 238 status = append(status, release.Status_DEPLOYED) 239 } 240 if l.deleted { 241 status = append(status, release.Status_DELETED) 242 } 243 if l.deleting { 244 status = append(status, release.Status_DELETING) 245 } 246 if l.failed { 247 status = append(status, release.Status_FAILED) 248 } 249 if l.superseded { 250 status = append(status, release.Status_SUPERSEDED) 251 } 252 if l.pending { 253 status = append(status, release.Status_PENDING_INSTALL, release.Status_PENDING_UPGRADE, release.Status_PENDING_ROLLBACK) 254 } 255 256 // Default case. 257 if len(status) == 0 { 258 status = append(status, release.Status_DEPLOYED, release.Status_FAILED) 259 } 260 return status 261 } 262 263 func getListResult(rels []*release.Release, next string) listResult { 264 listReleases := []listRelease{} 265 for _, r := range rels { 266 md := r.GetChart().GetMetadata() 267 t := "-" 268 if tspb := r.GetInfo().GetLastDeployed(); tspb != nil { 269 t = timeconv.String(tspb) 270 } 271 272 lr := listRelease{ 273 Name: r.GetName(), 274 Revision: r.GetVersion(), 275 Updated: t, 276 Status: r.GetInfo().GetStatus().GetCode().String(), 277 Chart: fmt.Sprintf("%s-%s", md.GetName(), md.GetVersion()), 278 AppVersion: md.GetAppVersion(), 279 Namespace: r.GetNamespace(), 280 } 281 listReleases = append(listReleases, lr) 282 } 283 284 return listResult{ 285 Releases: listReleases, 286 Next: next, 287 } 288 } 289 290 func shortenListResult(result listResult) []string { 291 names := []string{} 292 for _, r := range result.Releases { 293 names = append(names, r.Name) 294 } 295 296 return names 297 } 298 299 func formatResult(format string, short bool, result listResult, colWidth uint) (string, error) { 300 var output string 301 var err error 302 303 var shortResult []string 304 var finalResult interface{} 305 if short { 306 shortResult = shortenListResult(result) 307 finalResult = shortResult 308 } else { 309 finalResult = result 310 } 311 312 switch format { 313 case "": 314 if short { 315 output = formatTextShort(shortResult) 316 } else { 317 output = formatText(result, colWidth) 318 } 319 case "json": 320 o, e := json.Marshal(finalResult) 321 if e != nil { 322 err = fmt.Errorf("Failed to Marshal JSON output: %s", e) 323 } else { 324 output = string(o) 325 } 326 case "yaml": 327 o, e := yaml.Marshal(finalResult) 328 if e != nil { 329 err = fmt.Errorf("Failed to Marshal YAML output: %s", e) 330 } else { 331 output = string(o) 332 } 333 default: 334 err = fmt.Errorf("Unknown output format \"%s\"", format) 335 } 336 return output, err 337 } 338 339 func formatText(result listResult, colWidth uint) string { 340 nextOutput := "" 341 if result.Next != "" { 342 nextOutput = fmt.Sprintf("\tnext: %s\n", result.Next) 343 } 344 345 table := uitable.New() 346 table.MaxColWidth = colWidth 347 table.AddRow("NAME", "REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION", "NAMESPACE") 348 for _, lr := range result.Releases { 349 table.AddRow(lr.Name, lr.Revision, lr.Updated, lr.Status, lr.Chart, lr.AppVersion, lr.Namespace) 350 } 351 352 return fmt.Sprintf("%s%s", nextOutput, table.String()) 353 } 354 355 func formatTextShort(shortResult []string) string { 356 return strings.Join(shortResult, "\n") 357 }