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