Browse Source

ctlv3: support "write-out" for "endpoint health" command

Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Gyuho Lee 7 years ago
parent
commit
d019d3141a

+ 20 - 8
etcdctl/ctlv3/command/ep_command.go

@@ -76,9 +76,17 @@ func newEpHashKVCommand() *cobra.Command {
 	return hc
 	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.
 // epHealthCommandFunc executes the "endpoint-health" command.
 func epHealthCommandFunc(cmd *cobra.Command, args []string) {
 func epHealthCommandFunc(cmd *cobra.Command, args []string) {
 	flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags())
 	flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags())
+	initDisplayFromCmd(cmd)
 
 
 	sec := secureCfgFromCmd(cmd)
 	sec := secureCfgFromCmd(cmd)
 	dt := dialTimeoutFromCmd(cmd)
 	dt := dialTimeoutFromCmd(cmd)
@@ -95,7 +103,7 @@ func epHealthCommandFunc(cmd *cobra.Command, args []string) {
 	}
 	}
 
 
 	var wg sync.WaitGroup
 	var wg sync.WaitGroup
-	errc := make(chan error, len(cfgs))
+	hch := make(chan epHealth, len(cfgs))
 	for _, cfg := range cfgs {
 	for _, cfg := range cfgs {
 		wg.Add(1)
 		wg.Add(1)
 		go func(cfg *v3.Config) {
 		go func(cfg *v3.Config) {
@@ -103,7 +111,7 @@ func epHealthCommandFunc(cmd *cobra.Command, args []string) {
 			ep := cfg.Endpoints[0]
 			ep := cfg.Endpoints[0]
 			cli, err := v3.New(*cfg)
 			cli, err := v3.New(*cfg)
 			if err != nil {
 			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
 				return
 			}
 			}
 			st := time.Now()
 			st := time.Now()
@@ -112,25 +120,29 @@ func epHealthCommandFunc(cmd *cobra.Command, args []string) {
 			ctx, cancel := commandCtx(cmd)
 			ctx, cancel := commandCtx(cmd)
 			_, err = cli.Get(ctx, "health")
 			_, err = cli.Get(ctx, "health")
 			cancel()
 			cancel()
+			eh := epHealth{Ep: ep, Health: false, Took: time.Since(st).String()}
 			// permission denied is OK since proposal goes through consensus to get it
 			// permission denied is OK since proposal goes through consensus to get it
 			if err == nil || err == rpctypes.ErrPermissionDenied {
 			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 {
 			} else {
-				errc <- fmt.Errorf("%s is unhealthy: failed to commit proposal: %v", ep, err)
+				eh.Error = err.Error()
 			}
 			}
+			hch <- eh
 		}(cfg)
 		}(cfg)
 	}
 	}
 
 
 	wg.Wait()
 	wg.Wait()
-	close(errc)
+	close(hch)
 
 
 	errs := false
 	errs := false
-	for err := range errc {
-		if err != nil {
+	healthList := []epHealth{}
+	for h := range hch {
+		healthList = append(healthList, h)
+		if h.Error != "" {
 			errs = true
 			errs = true
-			fmt.Fprintln(os.Stderr, err)
 		}
 		}
 	}
 	}
+	display.EndpointHealth(healthList)
 	if errs {
 	if errs {
 		ExitWithError(ExitError, fmt.Errorf("unhealthy cluster"))
 		ExitWithError(ExitError, fmt.Errorf("unhealthy cluster"))
 	}
 	}

+ 15 - 0
etcdctl/ctlv3/command/printer.go

@@ -44,6 +44,7 @@ type printer interface {
 	MemberUpdate(id uint64, r v3.MemberUpdateResponse)
 	MemberUpdate(id uint64, r v3.MemberUpdateResponse)
 	MemberList(v3.MemberListResponse)
 	MemberList(v3.MemberListResponse)
 
 
+	EndpointHealth([]epHealth)
 	EndpointStatus([]epStatus)
 	EndpointStatus([]epStatus)
 	EndpointHashKV([]epHashKV)
 	EndpointHashKV([]epHashKV)
 	MoveLeader(leader, target uint64, r v3.MoveLeaderResponse)
 	MoveLeader(leader, target uint64, r v3.MoveLeaderResponse)
@@ -149,6 +150,7 @@ func newPrinterUnsupported(n string) printer {
 	return &printerUnsupported{printerRPC{nil, f}}
 	return &printerUnsupported{printerRPC{nil, f}}
 }
 }
 
 
+func (p *printerUnsupported) EndpointHealth([]epHealth) { p.p(nil) }
 func (p *printerUnsupported) EndpointStatus([]epStatus) { p.p(nil) }
 func (p *printerUnsupported) EndpointStatus([]epStatus) { p.p(nil) }
 func (p *printerUnsupported) EndpointHashKV([]epHashKV) { p.p(nil) }
 func (p *printerUnsupported) EndpointHashKV([]epHashKV) { p.p(nil) }
 func (p *printerUnsupported) DBStatus(snapshot.Status)  { 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
 	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) {
 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"}
 	hdr = []string{"endpoint", "ID", "version", "db size", "is leader", "raft term", "raft index", "raft applied index", "errors"}
 	for _, status := range statusList {
 	for _, status := range statusList {

+ 10 - 0
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) {
 func (p *fieldsPrinter) EndpointStatus(eps []epStatus) {
 	for _, ep := range eps {
 	for _, ep := range eps {
 		p.hdr(ep.Resp.Header)
 		p.hdr(ep.Resp.Header)

+ 1 - 0
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) EndpointStatus(r []epStatus) { printJSON(r) }
 func (p *jsonPrinter) EndpointHashKV(r []epHashKV) { printJSON(r) }
 func (p *jsonPrinter) EndpointHashKV(r []epHashKV) { printJSON(r) }
 func (p *jsonPrinter) DBStatus(r snapshot.Status)  { printJSON(r) }
 func (p *jsonPrinter) DBStatus(r snapshot.Status)  { printJSON(r) }

+ 11 - 0
etcdctl/ctlv3/command/printer_simple.go

@@ -16,6 +16,7 @@ package command
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"os"
 	"strings"
 	"strings"
 
 
 	v3 "github.com/coreos/etcd/clientv3"
 	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) {
 func (s *simplePrinter) EndpointStatus(statusList []epStatus) {
 	_, rows := makeEndpointStatusTable(statusList)
 	_, rows := makeEndpointStatusTable(statusList)
 	for _, row := range rows {
 	for _, row := range rows {

+ 10 - 0
etcdctl/ctlv3/command/printer_table.go

@@ -35,6 +35,16 @@ func (tp *tablePrinter) MemberList(r v3.MemberListResponse) {
 	table.SetAlignment(tablewriter.ALIGN_RIGHT)
 	table.SetAlignment(tablewriter.ALIGN_RIGHT)
 	table.Render()
 	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) {
 func (tp *tablePrinter) EndpointStatus(r []epStatus) {
 	hdr, rows := makeEndpointStatusTable(r)
 	hdr, rows := makeEndpointStatusTable(r)
 	table := tablewriter.NewWriter(os.Stdout)
 	table := tablewriter.NewWriter(os.Stdout)