Merge pull request #5991 from thomastaylor312/ref/history

ref(*): Refactors the history action to return releases
pull/6008/head
Taylor Thomas 5 years ago committed by GitHub
commit b4bf3526c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -21,9 +21,13 @@ import (
"io"
"github.com/spf13/cobra"
"github.com/gosuri/uitable"
"helm.sh/helm/cmd/helm/require"
"helm.sh/helm/pkg/action"
"helm.sh/helm/pkg/releaseutil"
"helm.sh/helm/pkg/chart"
"helm.sh/helm/pkg/release"
)
var historyHelp = `
@ -52,7 +56,7 @@ func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Aliases: []string{"hist"},
Args: require.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
history, err := client.Run(args[0])
history, err := getHistory(client, args[0])
if err != nil {
return err
}
@ -67,3 +71,115 @@ func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return cmd
}
type releaseInfo struct {
Revision int `json:"revision"`
Updated string `json:"updated"`
Status string `json:"status"`
Chart string `json:"chart"`
AppVersion string `json:"app_version"`
Description string `json:"description"`
}
type releaseHistory []releaseInfo
func marshalHistory(format action.OutputFormat, hist releaseHistory) (byt []byte, err error) {
switch format {
case action.YAML, action.JSON:
byt, err = format.Marshal(hist)
case action.Table:
byt, err = format.MarshalTable(func(tbl *uitable.Table) {
tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION", "DESCRIPTION")
for i := 0; i <= len(hist)-1; i++ {
r := hist[i]
tbl.AddRow(r.Revision, r.Updated, r.Status, r.Chart, r.AppVersion, r.Description)
}
})
default:
err = action.ErrInvalidFormatType
}
return
}
func getHistory(client *action.History, name string) (string, error) {
hist, err := client.Run(name)
if err != nil {
return "", err
}
releaseutil.Reverse(hist, releaseutil.SortByRevision)
var rels []*release.Release
for i := 0; i < min(len(hist), client.Max); i++ {
rels = append(rels, hist[i])
}
if len(rels) == 0 {
return "", nil
}
releaseHistory := getReleaseHistory(rels)
outputFormat, err := action.ParseOutputFormat(client.OutputFormat)
if err != nil {
return "", err
}
history, formattingError := marshalHistory(outputFormat, releaseHistory)
if formattingError != nil {
return "", formattingError
}
return string(history), nil
}
func getReleaseHistory(rls []*release.Release) (history releaseHistory) {
for i := len(rls) - 1; i >= 0; i-- {
r := rls[i]
c := formatChartname(r.Chart)
s := r.Info.Status.String()
v := r.Version
d := r.Info.Description
a := formatAppVersion(r.Chart)
rInfo := releaseInfo{
Revision: v,
Status: s,
Chart: c,
AppVersion: a,
Description: d,
}
if !r.Info.LastDeployed.IsZero() {
rInfo.Updated = r.Info.LastDeployed.String()
}
history = append(history, rInfo)
}
return history
}
func formatChartname(c *chart.Chart) string {
if c == nil || c.Metadata == nil {
// This is an edge case that has happened in prod, though we don't
// know how: https://github.com/helm/helm/issues/1347
return "MISSING"
}
return fmt.Sprintf("%s-%s", c.Name(), c.Metadata.Version)
}
func formatAppVersion(c *chart.Chart) string {
if c == nil || c.Metadata == nil {
// This is an edge case that has happened in prod, though we don't
// know how: https://github.com/helm/helm/issues/1347
return "MISSING"
}
return c.AppVersion()
}
func min(x, y int) int {
if x < y {
return x
}
return y
}

@ -17,10 +17,8 @@ limitations under the License.
package main
import (
"encoding/json"
"io"
"github.com/ghodss/yaml"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@ -66,17 +64,10 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
case "":
action.PrintRelease(out, rel)
return nil
case action.JSON:
data, err := json.Marshal(rel)
case action.JSON, action.YAML:
data, err := outfmt.Marshal(rel)
if err != nil {
return errors.Wrap(err, "failed to Marshal JSON output")
}
out.Write(data)
return nil
case action.YAML:
data, err := yaml.Marshal(rel)
if err != nil {
return errors.Wrap(err, "failed to Marshal YAML output")
return errors.Wrap(err, "failed to Marshal output")
}
out.Write(data)
return nil

@ -17,71 +17,11 @@ limitations under the License.
package action
import (
"encoding/json"
"fmt"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable"
"github.com/pkg/errors"
"helm.sh/helm/pkg/chart"
"helm.sh/helm/pkg/release"
"helm.sh/helm/pkg/releaseutil"
)
type releaseInfo struct {
Revision int `json:"revision"`
Updated string `json:"updated"`
Status string `json:"status"`
Chart string `json:"chart"`
AppVersion string `json:"app_version"`
Description string `json:"description"`
}
type releaseHistory []releaseInfo
type OutputFormat string
const (
Table OutputFormat = "table"
JSON OutputFormat = "json"
YAML OutputFormat = "yaml"
)
var ErrInvalidFormatType = errors.New("invalid format type")
func (o OutputFormat) String() string {
return string(o)
}
func ParseOutputFormat(s string) (out OutputFormat, err error) {
switch s {
case Table.String():
out, err = Table, nil
case JSON.String():
out, err = JSON, nil
case YAML.String():
out, err = YAML, nil
default:
out, err = "", ErrInvalidFormatType
}
return
}
func (o OutputFormat) MarshalHistory(hist releaseHistory) (byt []byte, err error) {
switch o {
case YAML:
byt, err = yaml.Marshal(hist)
case JSON:
byt, err = json.Marshal(hist)
case Table:
byt = formatAsTable(hist)
default:
err = ErrInvalidFormatType
}
return
}
// History is the action for checking the release's ledger.
//
// It provides the implementation of 'helm history'.
@ -100,93 +40,11 @@ func NewHistory(cfg *Configuration) *History {
}
// Run executes 'helm history' against the given release.
func (h *History) Run(name string) (string, error) {
func (h *History) Run(name string) ([]*release.Release, error) {
if err := validateReleaseName(name); err != nil {
return "", errors.Errorf("getHistory: Release name is invalid: %s", name)
return nil, errors.Errorf("release name is invalid: %s", name)
}
h.cfg.Log("getting history for release %s", name)
hist, err := h.cfg.Releases.History(name)
if err != nil {
return "", err
}
releaseutil.Reverse(hist, releaseutil.SortByRevision)
var rels []*release.Release
for i := 0; i < min(len(hist), h.Max); i++ {
rels = append(rels, hist[i])
}
if len(rels) == 0 {
return "", nil
}
releaseHistory := getReleaseHistory(rels)
outputFormat, err := ParseOutputFormat(h.OutputFormat)
if err != nil {
return "", err
}
history, formattingError := outputFormat.MarshalHistory(releaseHistory)
if formattingError != nil {
return "", formattingError
}
return string(history), nil
}
func getReleaseHistory(rls []*release.Release) (history releaseHistory) {
for i := len(rls) - 1; i >= 0; i-- {
r := rls[i]
c := formatChartname(r.Chart)
s := r.Info.Status.String()
v := r.Version
d := r.Info.Description
a := formatAppVersion(r.Chart)
rInfo := releaseInfo{
Revision: v,
Status: s,
Chart: c,
AppVersion: a,
Description: d,
}
if !r.Info.LastDeployed.IsZero() {
rInfo.Updated = r.Info.LastDeployed.String()
}
history = append(history, rInfo)
}
return history
}
func formatAsTable(releases releaseHistory) []byte {
tbl := uitable.New()
tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION", "DESCRIPTION")
for i := 0; i <= len(releases)-1; i++ {
r := releases[i]
tbl.AddRow(r.Revision, r.Updated, r.Status, r.Chart, r.AppVersion, r.Description)
}
return tbl.Bytes()
}
func formatChartname(c *chart.Chart) string {
if c == nil || c.Metadata == nil {
// This is an edge case that has happened in prod, though we don't
// know how: https://github.com/helm/helm/issues/1347
return "MISSING"
}
return fmt.Sprintf("%s-%s", c.Name(), c.Metadata.Version)
}
func formatAppVersion(c *chart.Chart) string {
if c == nil || c.Metadata == nil {
// This is an edge case that has happened in prod, though we don't
// know how: https://github.com/helm/helm/issues/1347
return "MISSING"
}
return c.AppVersion()
return h.cfg.Releases.History(name)
}

@ -0,0 +1,73 @@
package action
import (
"fmt"
"encoding/json"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable"
)
// OutputFormat is a type for capturing supported output formats
type OutputFormat string
// TableFunc is a function that can be used to add rows to a table
type TableFunc func(tbl *uitable.Table)
const (
Table OutputFormat = "table"
JSON OutputFormat = "json"
YAML OutputFormat = "yaml"
)
// ErrInvalidFormatType is returned when an unsupported format type is used
var ErrInvalidFormatType = fmt.Errorf("invalid format type")
// String returns the string reprsentation of the OutputFormat
func (o OutputFormat) String() string {
return string(o)
}
// Marshal uses the specified output format to marshal out the given data. It
// does not support tabular output. For tabular output, use MarshalTable
func (o OutputFormat) Marshal(data interface{}) (byt []byte, err error) {
switch o {
case YAML:
byt, err = yaml.Marshal(data)
case JSON:
byt, err = json.Marshal(data)
default:
err = ErrInvalidFormatType
}
return
}
// MarshalTable returns a formatted table using the given headers. Rows can be
// added to the table using the given TableFunc
func (o OutputFormat) MarshalTable(f TableFunc) ([]byte, error) {
if o != Table {
return nil, ErrInvalidFormatType
}
tbl := uitable.New()
if f == nil {
return []byte{}, nil
}
f(tbl)
return tbl.Bytes(), nil
}
// ParseOutputFormat takes a raw string and returns the matching OutputFormat.
// If the format does not exists, ErrInvalidFormatType is returned
func ParseOutputFormat(s string) (out OutputFormat, err error) {
switch s {
case Table.String():
out, err = Table, nil
case JSON.String():
out, err = JSON, nil
case YAML.String():
out, err = YAML, nil
default:
out, err = "", ErrInvalidFormatType
}
return
}

@ -1,24 +0,0 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package action
func min(x, y int) int {
if x < y {
return x
}
return y
}
Loading…
Cancel
Save