github.com/Azure/draft-classic@v0.16.0/cmd/draft/history.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"github.com/Azure/draft/pkg/local"
     8  	"github.com/Azure/draft/pkg/storage"
     9  	"github.com/Azure/draft/pkg/storage/kube/configmap"
    10  	"github.com/ghodss/yaml"
    11  	"github.com/gosuri/uitable"
    12  	"github.com/spf13/cobra"
    13  	"golang.org/x/net/context"
    14  	"io"
    15  	"k8s.io/helm/pkg/timeconv"
    16  )
    17  
    18  const historyDesc = `Display the build history of a Draft application.`
    19  
    20  type historyCmd struct {
    21  	out      io.Writer
    22  	fmt      string
    23  	env      string
    24  	max      int64
    25  	pretty   bool
    26  	colWidth uint
    27  }
    28  
    29  func newHistoryCmd(out io.Writer) *cobra.Command {
    30  	hc := &historyCmd{out: out}
    31  	cmd := &cobra.Command{
    32  		Use:   "history",
    33  		Short: historyDesc,
    34  		Long:  historyDesc,
    35  		RunE: func(cmd *cobra.Command, args []string) error {
    36  			return hc.run()
    37  		},
    38  	}
    39  
    40  	f := cmd.Flags()
    41  	f.Int64Var(&hc.max, "max", 256, "maximum number of results to include in history")
    42  	f.UintVar(&hc.colWidth, "col-width", 60, "specifies the max column width of output")
    43  	f.BoolVar(&hc.pretty, "pretty", false, "pretty print output")
    44  	f.StringVarP(&hc.fmt, "output", "o", "table", "prints the output in the specified format (json|table|yaml)")
    45  	f.StringVarP(&hc.env, environmentFlagName, environmentFlagShorthand, defaultDraftEnvironment(), environmentFlagUsage)
    46  	return cmd
    47  }
    48  
    49  func (cmd *historyCmd) run() error {
    50  	app, err := local.DeployedApplication(draftToml, cmd.env)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	client, _, err := getKubeClient(kubeContext)
    55  	if err != nil {
    56  		return fmt.Errorf("Could not get a kube client: %v", err)
    57  	}
    58  	store := configmap.NewConfigMaps(client.CoreV1().ConfigMaps(tillerNamespace))
    59  
    60  	// get history from store
    61  	h, err := getHistory(context.Background(), store, app.Name, cmd.max)
    62  	if err != nil {
    63  		return err
    64  	}
    65  	if len(h) > 0 {
    66  		var output []byte
    67  		switch bh := toBuildHistory(h); cmd.fmt {
    68  		case "yaml":
    69  			if output, err = yaml.Marshal(&bh); err != nil {
    70  				return err
    71  			}
    72  		case "json":
    73  			if output, err = json.Marshal(&bh); err != nil {
    74  				return err
    75  			}
    76  			if cmd.pretty {
    77  				var b bytes.Buffer
    78  				json.Indent(&b, output, "", "  ")
    79  				output = b.Bytes()
    80  			}
    81  		case "table":
    82  			output = formatTable(bh, cmd.colWidth)
    83  		default:
    84  			return fmt.Errorf("unknown output format %q", cmd.fmt)
    85  		}
    86  		fmt.Fprintln(cmd.out, string(output))
    87  	}
    88  	return nil
    89  }
    90  
    91  func getHistory(ctx context.Context, store storage.Store, app string, max int64) (h []*storage.Object, err error) {
    92  	if h, err = store.GetBuilds(ctx, app); err != nil {
    93  		return nil, fmt.Errorf("failed to retrieve application (%q) build history from storage: %v", app, err)
    94  	}
    95  	// For deterministic return of history results we sort by the storage
    96  	// object's created at timestamp.
    97  	storage.SortByCreatedAt(h)
    98  
    99  	min := func(x, y int) int {
   100  		if x < y {
   101  			return x
   102  		}
   103  		return y
   104  	}
   105  
   106  	return h[:min(len(h), int(max))], nil
   107  }
   108  
   109  type buildHistory []buildInfo
   110  
   111  type buildInfo struct {
   112  	BuildID string `json:"buildID"`
   113  	Release string `json:"release"`
   114  	Context string `json:"context"`
   115  	Created string `json:"createdAt"`
   116  }
   117  
   118  func toBuildHistory(ls []*storage.Object) (h buildHistory) {
   119  	orElse := func(str, def string) string {
   120  		if str != "" {
   121  			return str
   122  		}
   123  		return def
   124  	}
   125  	for i := len(ls) - 1; i >= 0; i-- {
   126  		rls := orElse(ls[i].GetRelease(), "-")
   127  		ctx := ls[i].GetContextID()
   128  		h = append(h, buildInfo{
   129  			BuildID: ls[i].GetBuildID(),
   130  			Release: rls,
   131  			Context: fmt.Sprintf("%X", ctx[len(ctx)-5:]),
   132  			Created: timeconv.String(ls[i].GetCreatedAt()),
   133  		})
   134  	}
   135  	return h
   136  }
   137  
   138  func formatTable(h buildHistory, w uint) []byte {
   139  	tbl := uitable.New()
   140  	tbl.MaxColWidth = w
   141  	tbl.AddRow("BUILD_ID", "CONTEXT_ID", "CREATED_AT", "RELEASE")
   142  	for i := 0; i < len(h); i++ {
   143  		b := h[i]
   144  		tbl.AddRow(b.BuildID, b.Context, b.Created, b.Release)
   145  	}
   146  	return tbl.Bytes()
   147  }