github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/cmd/helm/history.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 "strconv" 23 "time" 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/chart" 31 "github.com/stefanmcshane/helm/pkg/cli/output" 32 "github.com/stefanmcshane/helm/pkg/release" 33 "github.com/stefanmcshane/helm/pkg/releaseutil" 34 helmtime "github.com/stefanmcshane/helm/pkg/time" 35 ) 36 37 var historyHelp = ` 38 History prints historical revisions for a given release. 39 40 A default maximum of 256 revisions will be returned. Setting '--max' 41 configures the maximum length of the revision list returned. 42 43 The historical release set is printed as a formatted table, e.g: 44 45 $ helm history angry-bird 46 REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION 47 1 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Initial install 48 2 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Upgraded successfully 49 3 Mon Oct 3 10:15:13 2016 superseded alpine-0.1.0 1.0 Rolled back to 2 50 4 Mon Oct 3 10:15:13 2016 deployed alpine-0.1.0 1.0 Upgraded successfully 51 ` 52 53 func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { 54 client := action.NewHistory(cfg) 55 var outfmt output.Format 56 57 cmd := &cobra.Command{ 58 Use: "history RELEASE_NAME", 59 Long: historyHelp, 60 Short: "fetch release history", 61 Aliases: []string{"hist"}, 62 Args: require.ExactArgs(1), 63 ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 64 if len(args) != 0 { 65 return nil, cobra.ShellCompDirectiveNoFileComp 66 } 67 return compListReleases(toComplete, args, cfg) 68 }, 69 RunE: func(cmd *cobra.Command, args []string) error { 70 history, err := getHistory(client, args[0]) 71 if err != nil { 72 return err 73 } 74 75 return outfmt.Write(out, history) 76 }, 77 } 78 79 f := cmd.Flags() 80 f.IntVar(&client.Max, "max", 256, "maximum number of revision to include in history") 81 bindOutputFlag(cmd, &outfmt) 82 83 return cmd 84 } 85 86 type releaseInfo struct { 87 Revision int `json:"revision"` 88 Updated helmtime.Time `json:"updated"` 89 Status string `json:"status"` 90 Chart string `json:"chart"` 91 AppVersion string `json:"app_version"` 92 Description string `json:"description"` 93 } 94 95 type releaseHistory []releaseInfo 96 97 func (r releaseHistory) WriteJSON(out io.Writer) error { 98 return output.EncodeJSON(out, r) 99 } 100 101 func (r releaseHistory) WriteYAML(out io.Writer) error { 102 return output.EncodeYAML(out, r) 103 } 104 105 func (r releaseHistory) WriteTable(out io.Writer) error { 106 tbl := uitable.New() 107 tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION", "DESCRIPTION") 108 for _, item := range r { 109 tbl.AddRow(item.Revision, item.Updated.Format(time.ANSIC), item.Status, item.Chart, item.AppVersion, item.Description) 110 } 111 return output.EncodeTable(out, tbl) 112 } 113 114 func getHistory(client *action.History, name string) (releaseHistory, error) { 115 hist, err := client.Run(name) 116 if err != nil { 117 return nil, err 118 } 119 120 releaseutil.Reverse(hist, releaseutil.SortByRevision) 121 122 var rels []*release.Release 123 for i := 0; i < min(len(hist), client.Max); i++ { 124 rels = append(rels, hist[i]) 125 } 126 127 if len(rels) == 0 { 128 return releaseHistory{}, nil 129 } 130 131 releaseHistory := getReleaseHistory(rels) 132 133 return releaseHistory, nil 134 } 135 136 func getReleaseHistory(rls []*release.Release) (history releaseHistory) { 137 for i := len(rls) - 1; i >= 0; i-- { 138 r := rls[i] 139 c := formatChartname(r.Chart) 140 s := r.Info.Status.String() 141 v := r.Version 142 d := r.Info.Description 143 a := formatAppVersion(r.Chart) 144 145 rInfo := releaseInfo{ 146 Revision: v, 147 Status: s, 148 Chart: c, 149 AppVersion: a, 150 Description: d, 151 } 152 if !r.Info.LastDeployed.IsZero() { 153 rInfo.Updated = r.Info.LastDeployed 154 155 } 156 history = append(history, rInfo) 157 } 158 159 return history 160 } 161 162 func formatChartname(c *chart.Chart) string { 163 if c == nil || c.Metadata == nil { 164 // This is an edge case that has happened in prod, though we don't 165 // know how: https://github.com/helm/helm/issues/1347 166 return "MISSING" 167 } 168 return fmt.Sprintf("%s-%s", c.Name(), c.Metadata.Version) 169 } 170 171 func formatAppVersion(c *chart.Chart) string { 172 if c == nil || c.Metadata == nil { 173 // This is an edge case that has happened in prod, though we don't 174 // know how: https://github.com/helm/helm/issues/1347 175 return "MISSING" 176 } 177 return c.AppVersion() 178 } 179 180 func min(x, y int) int { 181 if x < y { 182 return x 183 } 184 return y 185 } 186 187 func compListRevisions(toComplete string, cfg *action.Configuration, releaseName string) ([]string, cobra.ShellCompDirective) { 188 client := action.NewHistory(cfg) 189 190 var revisions []string 191 if hist, err := client.Run(releaseName); err == nil { 192 for _, release := range hist { 193 appVersion := fmt.Sprintf("App: %s", release.Chart.Metadata.AppVersion) 194 chartDesc := fmt.Sprintf("Chart: %s-%s", release.Chart.Metadata.Name, release.Chart.Metadata.Version) 195 revisions = append(revisions, fmt.Sprintf("%s\t%s, %s", strconv.Itoa(release.Version), appVersion, chartDesc)) 196 } 197 return revisions, cobra.ShellCompDirectiveNoFileComp 198 } 199 return nil, cobra.ShellCompDirectiveError 200 }