github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/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 "fmt" 21 "io" 22 "os" 23 "strconv" 24 25 "github.com/gosuri/uitable" 26 "github.com/spf13/cobra" 27 28 "github.com/stefanmcshane/helm/cmd/helm/require" 29 "github.com/stefanmcshane/helm/pkg/action" 30 "github.com/stefanmcshane/helm/pkg/cli/output" 31 "github.com/stefanmcshane/helm/pkg/release" 32 ) 33 34 var listHelp = ` 35 This command lists all of the releases for a specified namespace (uses current namespace context if namespace not specified). 36 37 By default, it lists only releases that are deployed or failed. Flags like 38 '--uninstalled' and '--all' will alter this behavior. Such flags can be combined: 39 '--uninstalled --failed'. 40 41 By default, items are sorted alphabetically. Use the '-d' flag to sort by 42 release date. 43 44 If the --filter flag is provided, it will be treated as a filter. Filters are 45 regular expressions (Perl compatible) that are applied to the list of releases. 46 Only items that match the filter will be returned. 47 48 $ helm list --filter 'ara[a-z]+' 49 NAME UPDATED CHART 50 maudlin-arachnid 2020-06-18 14:17:46.125134977 +0000 UTC alpine-0.1.0 51 52 If no results are found, 'helm list' will exit 0, but with no output (or in 53 the case of no '-q' flag, only headers). 54 55 By default, up to 256 items may be returned. To limit this, use the '--max' flag. 56 Setting '--max' to 0 will not return all results. Rather, it will return the 57 server's default, which may be much higher than 256. Pairing the '--max' 58 flag with the '--offset' flag allows you to page through results. 59 ` 60 61 func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { 62 client := action.NewList(cfg) 63 var outfmt output.Format 64 65 cmd := &cobra.Command{ 66 Use: "list", 67 Short: "list releases", 68 Long: listHelp, 69 Aliases: []string{"ls"}, 70 Args: require.NoArgs, 71 ValidArgsFunction: noCompletions, 72 RunE: func(cmd *cobra.Command, args []string) error { 73 if client.AllNamespaces { 74 if err := cfg.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), debug); err != nil { 75 return err 76 } 77 } 78 client.SetStateMask() 79 80 results, err := client.Run() 81 if err != nil { 82 return err 83 } 84 85 if client.Short { 86 names := make([]string, 0, len(results)) 87 for _, res := range results { 88 names = append(names, res.Name) 89 } 90 91 outputFlag := cmd.Flag("output") 92 93 switch outputFlag.Value.String() { 94 case "json": 95 output.EncodeJSON(out, names) 96 return nil 97 case "yaml": 98 output.EncodeYAML(out, names) 99 return nil 100 case "table": 101 for _, res := range results { 102 fmt.Fprintln(out, res.Name) 103 } 104 return nil 105 } 106 } 107 108 return outfmt.Write(out, newReleaseListWriter(results, client.TimeFormat, client.NoHeaders)) 109 }, 110 } 111 112 f := cmd.Flags() 113 f.BoolVarP(&client.Short, "short", "q", false, "output short (quiet) listing format") 114 f.BoolVarP(&client.NoHeaders, "no-headers", "", false, "don't print headers when using the default output format") 115 f.StringVar(&client.TimeFormat, "time-format", "", `format time using golang time formatter. Example: --time-format "2006-01-02 15:04:05Z0700"`) 116 f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date") 117 f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order") 118 f.BoolVarP(&client.All, "all", "a", false, "show all releases without any filter applied") 119 f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases (if 'helm uninstall --keep-history' was used)") 120 f.BoolVar(&client.Superseded, "superseded", false, "show superseded releases") 121 f.BoolVar(&client.Uninstalling, "uninstalling", false, "show releases that are currently being uninstalled") 122 f.BoolVar(&client.Deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled") 123 f.BoolVar(&client.Failed, "failed", false, "show failed releases") 124 f.BoolVar(&client.Pending, "pending", false, "show pending releases") 125 f.BoolVarP(&client.AllNamespaces, "all-namespaces", "A", false, "list releases across all namespaces") 126 f.IntVarP(&client.Limit, "max", "m", 256, "maximum number of releases to fetch") 127 f.IntVar(&client.Offset, "offset", 0, "next release index in the list, used to offset from start value") 128 f.StringVarP(&client.Filter, "filter", "f", "", "a regular expression (Perl compatible). Any releases that match the expression will be included in the results") 129 f.StringVarP(&client.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Works only for secret(default) and configmap storage backends.") 130 bindOutputFlag(cmd, &outfmt) 131 132 return cmd 133 } 134 135 type releaseElement struct { 136 Name string `json:"name"` 137 Namespace string `json:"namespace"` 138 Revision string `json:"revision"` 139 Updated string `json:"updated"` 140 Status string `json:"status"` 141 Chart string `json:"chart"` 142 AppVersion string `json:"app_version"` 143 } 144 145 type releaseListWriter struct { 146 releases []releaseElement 147 noHeaders bool 148 } 149 150 func newReleaseListWriter(releases []*release.Release, timeFormat string, noHeaders bool) *releaseListWriter { 151 // Initialize the array so no results returns an empty array instead of null 152 elements := make([]releaseElement, 0, len(releases)) 153 for _, r := range releases { 154 element := releaseElement{ 155 Name: r.Name, 156 Namespace: r.Namespace, 157 Revision: strconv.Itoa(r.Version), 158 Status: r.Info.Status.String(), 159 Chart: formatChartname(r.Chart), 160 AppVersion: formatAppVersion(r.Chart), 161 } 162 163 t := "-" 164 if tspb := r.Info.LastDeployed; !tspb.IsZero() { 165 if timeFormat != "" { 166 t = tspb.Format(timeFormat) 167 } else { 168 t = tspb.String() 169 } 170 } 171 element.Updated = t 172 173 elements = append(elements, element) 174 } 175 return &releaseListWriter{elements, noHeaders} 176 } 177 178 func (r *releaseListWriter) WriteTable(out io.Writer) error { 179 table := uitable.New() 180 if !r.noHeaders { 181 table.AddRow("NAME", "NAMESPACE", "REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION") 182 } 183 for _, r := range r.releases { 184 table.AddRow(r.Name, r.Namespace, r.Revision, r.Updated, r.Status, r.Chart, r.AppVersion) 185 } 186 return output.EncodeTable(out, table) 187 } 188 189 func (r *releaseListWriter) WriteJSON(out io.Writer) error { 190 return output.EncodeJSON(out, r.releases) 191 } 192 193 func (r *releaseListWriter) WriteYAML(out io.Writer) error { 194 return output.EncodeYAML(out, r.releases) 195 } 196 197 // Returns all releases from 'releases', except those with names matching 'ignoredReleases' 198 func filterReleases(releases []*release.Release, ignoredReleaseNames []string) []*release.Release { 199 // if ignoredReleaseNames is nil, just return releases 200 if ignoredReleaseNames == nil { 201 return releases 202 } 203 204 var filteredReleases []*release.Release 205 for _, rel := range releases { 206 found := false 207 for _, ignoredName := range ignoredReleaseNames { 208 if rel.Name == ignoredName { 209 found = true 210 break 211 } 212 } 213 if !found { 214 filteredReleases = append(filteredReleases, rel) 215 } 216 } 217 218 return filteredReleases 219 } 220 221 // Provide dynamic auto-completion for release names 222 func compListReleases(toComplete string, ignoredReleaseNames []string, cfg *action.Configuration) ([]string, cobra.ShellCompDirective) { 223 cobra.CompDebugln(fmt.Sprintf("compListReleases with toComplete %s", toComplete), settings.Debug) 224 225 client := action.NewList(cfg) 226 client.All = true 227 client.Limit = 0 228 // Do not filter so as to get the entire list of releases. 229 // This will allow zsh and fish to match completion choices 230 // on other criteria then prefix. For example: 231 // helm status ingress<TAB> 232 // can match 233 // helm status nginx-ingress 234 // 235 // client.Filter = fmt.Sprintf("^%s", toComplete) 236 237 client.SetStateMask() 238 releases, err := client.Run() 239 if err != nil { 240 return nil, cobra.ShellCompDirectiveDefault 241 } 242 243 var choices []string 244 filteredReleases := filterReleases(releases, ignoredReleaseNames) 245 for _, rel := range filteredReleases { 246 choices = append(choices, 247 fmt.Sprintf("%s\t%s-%s -> %s", rel.Name, rel.Chart.Metadata.Name, rel.Chart.Metadata.Version, rel.Info.Status.String())) 248 } 249 250 return choices, cobra.ShellCompDirectiveNoFileComp 251 }