From d019d3141a469f1d0b06ffa4a50bc1b248d4f50d Mon Sep 17 00:00:00 2001 From: Gyuho Lee Date: Fri, 6 Apr 2018 11:31:00 -0700 Subject: [PATCH] ctlv3: support "write-out" for "endpoint health" command Signed-off-by: Gyuho Lee --- etcdctl/ctlv3/command/ep_command.go | 28 ++++++++++++++++++------- etcdctl/ctlv3/command/printer.go | 15 +++++++++++++ etcdctl/ctlv3/command/printer_fields.go | 10 +++++++++ etcdctl/ctlv3/command/printer_json.go | 1 + etcdctl/ctlv3/command/printer_simple.go | 11 ++++++++++ etcdctl/ctlv3/command/printer_table.go | 10 +++++++++ 6 files changed, 67 insertions(+), 8 deletions(-) diff --git a/etcdctl/ctlv3/command/ep_command.go b/etcdctl/ctlv3/command/ep_command.go index 88f0199fe..a45e24053 100644 --- a/etcdctl/ctlv3/command/ep_command.go +++ b/etcdctl/ctlv3/command/ep_command.go @@ -76,9 +76,17 @@ func newEpHashKVCommand() *cobra.Command { return hc } +type epHealth struct { + Ep string `json:"endpoint"` + Health bool `json:"health"` + Took string `json:"took"` + Error string `json:"error,omitempty"` +} + // epHealthCommandFunc executes the "endpoint-health" command. func epHealthCommandFunc(cmd *cobra.Command, args []string) { flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags()) + initDisplayFromCmd(cmd) sec := secureCfgFromCmd(cmd) dt := dialTimeoutFromCmd(cmd) @@ -95,7 +103,7 @@ func epHealthCommandFunc(cmd *cobra.Command, args []string) { } var wg sync.WaitGroup - errc := make(chan error, len(cfgs)) + hch := make(chan epHealth, len(cfgs)) for _, cfg := range cfgs { wg.Add(1) go func(cfg *v3.Config) { @@ -103,7 +111,7 @@ func epHealthCommandFunc(cmd *cobra.Command, args []string) { ep := cfg.Endpoints[0] cli, err := v3.New(*cfg) if err != nil { - errc <- fmt.Errorf("%s is unhealthy: failed to connect: %v", ep, err) + hch <- epHealth{Ep: ep, Health: false, Error: err.Error()} return } st := time.Now() @@ -112,25 +120,29 @@ func epHealthCommandFunc(cmd *cobra.Command, args []string) { ctx, cancel := commandCtx(cmd) _, err = cli.Get(ctx, "health") cancel() + eh := epHealth{Ep: ep, Health: false, Took: time.Since(st).String()} // permission denied is OK since proposal goes through consensus to get it if err == nil || err == rpctypes.ErrPermissionDenied { - fmt.Printf("%s is healthy: successfully committed proposal: took = %v\n", ep, time.Since(st)) + eh.Health = true } else { - errc <- fmt.Errorf("%s is unhealthy: failed to commit proposal: %v", ep, err) + eh.Error = err.Error() } + hch <- eh }(cfg) } wg.Wait() - close(errc) + close(hch) errs := false - for err := range errc { - if err != nil { + healthList := []epHealth{} + for h := range hch { + healthList = append(healthList, h) + if h.Error != "" { errs = true - fmt.Fprintln(os.Stderr, err) } } + display.EndpointHealth(healthList) if errs { ExitWithError(ExitError, fmt.Errorf("unhealthy cluster")) } diff --git a/etcdctl/ctlv3/command/printer.go b/etcdctl/ctlv3/command/printer.go index 057a24a05..35f84aaa4 100644 --- a/etcdctl/ctlv3/command/printer.go +++ b/etcdctl/ctlv3/command/printer.go @@ -44,6 +44,7 @@ type printer interface { MemberUpdate(id uint64, r v3.MemberUpdateResponse) MemberList(v3.MemberListResponse) + EndpointHealth([]epHealth) EndpointStatus([]epStatus) EndpointHashKV([]epHashKV) MoveLeader(leader, target uint64, r v3.MoveLeaderResponse) @@ -149,6 +150,7 @@ func newPrinterUnsupported(n string) printer { return &printerUnsupported{printerRPC{nil, f}} } +func (p *printerUnsupported) EndpointHealth([]epHealth) { p.p(nil) } func (p *printerUnsupported) EndpointStatus([]epStatus) { p.p(nil) } func (p *printerUnsupported) EndpointHashKV([]epHashKV) { p.p(nil) } func (p *printerUnsupported) DBStatus(snapshot.Status) { p.p(nil) } @@ -173,6 +175,19 @@ func makeMemberListTable(r v3.MemberListResponse) (hdr []string, rows [][]string return hdr, rows } +func makeEndpointHealthTable(healthList []epHealth) (hdr []string, rows [][]string) { + hdr = []string{"endpoint", "health", "took", "error"} + for _, h := range healthList { + rows = append(rows, []string{ + h.Ep, + fmt.Sprintf("%v", h.Health), + h.Took, + h.Error, + }) + } + return hdr, rows +} + func makeEndpointStatusTable(statusList []epStatus) (hdr []string, rows [][]string) { hdr = []string{"endpoint", "ID", "version", "db size", "is leader", "raft term", "raft index", "raft applied index", "errors"} for _, status := range statusList { diff --git a/etcdctl/ctlv3/command/printer_fields.go b/etcdctl/ctlv3/command/printer_fields.go index 7351aa739..a63ba0bea 100644 --- a/etcdctl/ctlv3/command/printer_fields.go +++ b/etcdctl/ctlv3/command/printer_fields.go @@ -141,6 +141,16 @@ func (p *fieldsPrinter) MemberList(r v3.MemberListResponse) { } } +func (p *fieldsPrinter) EndpointHealth(hs []epHealth) { + for _, h := range hs { + fmt.Printf("\"Endpoint\" : %q\n", h.Ep) + fmt.Println(`"Health" :`, h.Health) + fmt.Println(`"Took" :`, h.Took) + fmt.Println(`"Error" :`, h.Error) + fmt.Println() + } +} + func (p *fieldsPrinter) EndpointStatus(eps []epStatus) { for _, ep := range eps { p.hdr(ep.Resp.Header) diff --git a/etcdctl/ctlv3/command/printer_json.go b/etcdctl/ctlv3/command/printer_json.go index eb985aae7..c57f8ca0d 100644 --- a/etcdctl/ctlv3/command/printer_json.go +++ b/etcdctl/ctlv3/command/printer_json.go @@ -30,6 +30,7 @@ func newJSONPrinter() printer { } } +func (p *jsonPrinter) EndpointHealth(r []epHealth) { printJSON(r) } func (p *jsonPrinter) EndpointStatus(r []epStatus) { printJSON(r) } func (p *jsonPrinter) EndpointHashKV(r []epHashKV) { printJSON(r) } func (p *jsonPrinter) DBStatus(r snapshot.Status) { printJSON(r) } diff --git a/etcdctl/ctlv3/command/printer_simple.go b/etcdctl/ctlv3/command/printer_simple.go index 1b36b88cd..99b27b68c 100644 --- a/etcdctl/ctlv3/command/printer_simple.go +++ b/etcdctl/ctlv3/command/printer_simple.go @@ -16,6 +16,7 @@ package command import ( "fmt" + "os" "strings" v3 "github.com/coreos/etcd/clientv3" @@ -142,6 +143,16 @@ func (s *simplePrinter) MemberList(resp v3.MemberListResponse) { } } +func (s *simplePrinter) EndpointHealth(hs []epHealth) { + for _, h := range hs { + if h.Error == "" { + fmt.Fprintf(os.Stderr, "%s is healthy: successfully committed proposal: took = %v\n", h.Ep, h.Took) + } else { + fmt.Fprintf(os.Stderr, "%s is unhealthy: failed to commit proposal: %v", h.Ep, h.Error) + } + } +} + func (s *simplePrinter) EndpointStatus(statusList []epStatus) { _, rows := makeEndpointStatusTable(statusList) for _, row := range rows { diff --git a/etcdctl/ctlv3/command/printer_table.go b/etcdctl/ctlv3/command/printer_table.go index 0ab97b4e5..354119271 100644 --- a/etcdctl/ctlv3/command/printer_table.go +++ b/etcdctl/ctlv3/command/printer_table.go @@ -35,6 +35,16 @@ func (tp *tablePrinter) MemberList(r v3.MemberListResponse) { table.SetAlignment(tablewriter.ALIGN_RIGHT) table.Render() } +func (tp *tablePrinter) EndpointHealth(r []epHealth) { + hdr, rows := makeEndpointHealthTable(r) + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader(hdr) + for _, row := range rows { + table.Append(row) + } + table.SetAlignment(tablewriter.ALIGN_RIGHT) + table.Render() +} func (tp *tablePrinter) EndpointStatus(r []epStatus) { hdr, rows := makeEndpointStatusTable(r) table := tablewriter.NewWriter(os.Stdout)