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 }