From 5176b63fa0f4dc3f0fec0c010d7d8977fedec2a5 Mon Sep 17 00:00:00 2001 From: Gyu-Ho Lee Date: Tue, 1 Aug 2017 17:13:47 -0700 Subject: [PATCH] ctlv3: add 'endpoint hashkv' command Signed-off-by: Gyu-Ho Lee --- etcdctl/README.md | 43 +++++++++++++++++++++++++ etcdctl/ctlv3/command/ep_command.go | 41 +++++++++++++++++++++++ etcdctl/ctlv3/command/printer.go | 13 ++++++++ etcdctl/ctlv3/command/printer_fields.go | 9 ++++++ etcdctl/ctlv3/command/printer_json.go | 1 + etcdctl/ctlv3/command/printer_simple.go | 7 ++++ etcdctl/ctlv3/command/printer_table.go | 10 ++++++ 7 files changed, 124 insertions(+) diff --git a/etcdctl/README.md b/etcdctl/README.md index 942d23556..204536a33 100644 --- a/etcdctl/README.md +++ b/etcdctl/README.md @@ -641,6 +641,49 @@ Get the status for all endpoints in the cluster associated with the default endp +------------------------+------------------+----------------+---------+-----------+-----------+------------+ ``` +### ENDPOINT HASHKV + +ENDPOINT HASHKV fetches the hash of the key-value store of an endpoint. + +#### Output + +##### Simple format + +Prints a humanized table of each endpoint URL and KV history hash. + +##### JSON format + +Prints a line of JSON encoding each endpoint URL and KV history hash. + +#### Examples + +Get the hash for the default endpoint: + +```bash +./etcdctl endpoint hashkv +# 127.0.0.1:2379, 1084519789 +``` + +Get the status for the default endpoint as JSON: + +```bash +./etcdctl -w json endpoint hashkv +# [{"Endpoint":"127.0.0.1:2379","Hash":{"header":{"cluster_id":14841639068965178418,"member_id":10276657743932975437,"revision":1,"raft_term":3},"hash":1084519789,"compact_revision":-1}}] +``` + +Get the status for all endpoints in the cluster associated with the default endpoint: + +```bash +./etcdctl -w table endpoint --cluster hashkv ++------------------------+------------+ +| ENDPOINT | HASH | ++------------------------+------------+ +| http://127.0.0.1:12379 | 1084519789 | +| http://127.0.0.1:22379 | 1084519789 | +| http://127.0.0.1:32379 | 1084519789 | ++------------------------+------------+ +``` + ### ALARM \ Provides alarm related commands diff --git a/etcdctl/ctlv3/command/ep_command.go b/etcdctl/ctlv3/command/ep_command.go index d31de3c44..4fac7e44d 100644 --- a/etcdctl/ctlv3/command/ep_command.go +++ b/etcdctl/ctlv3/command/ep_command.go @@ -28,6 +28,7 @@ import ( ) var epClusterEndpoints bool +var epHashKVRev int64 // NewEndpointCommand returns the cobra command for "endpoint". func NewEndpointCommand() *cobra.Command { @@ -39,6 +40,7 @@ func NewEndpointCommand() *cobra.Command { ec.PersistentFlags().BoolVar(&epClusterEndpoints, "cluster", false, "use all endpoints from the cluster member list") ec.AddCommand(newEpHealthCommand()) ec.AddCommand(newEpStatusCommand()) + ec.AddCommand(newEpHashKVCommand()) return ec } @@ -64,6 +66,16 @@ The items in the lists are endpoint, ID, version, db size, is leader, raft term, } } +func newEpHashKVCommand() *cobra.Command { + hc := &cobra.Command{ + Use: "hashkv", + Short: "Prints the KV history hash for each endpoint in --endpoints", + Run: epHashKVCommandFunc, + } + hc.PersistentFlags().Int64Var(&epHashKVRev, "rev", 0, "maximum revision to hash (default: all revisions)") + return hc +} + // epHealthCommandFunc executes the "endpoint-health" command. func epHealthCommandFunc(cmd *cobra.Command, args []string) { flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags()) @@ -151,6 +163,35 @@ func epStatusCommandFunc(cmd *cobra.Command, args []string) { } } +type epHashKV struct { + Ep string `json:"Endpoint"` + Resp *v3.HashKVResponse `json:"HashKV"` +} + +func epHashKVCommandFunc(cmd *cobra.Command, args []string) { + c := mustClientFromCmd(cmd) + + hashList := []epHashKV{} + var err error + for _, ep := range endpointsFromCluster(cmd) { + ctx, cancel := commandCtx(cmd) + resp, serr := c.HashKV(ctx, ep, epHashKVRev) + cancel() + if serr != nil { + err = serr + fmt.Fprintf(os.Stderr, "Failed to get the hash of endpoint %s (%v)\n", ep, serr) + continue + } + hashList = append(hashList, epHashKV{Ep: ep, Resp: resp}) + } + + display.EndpointHashKV(hashList) + + if err != nil { + ExitWithError(ExitError, err) + } +} + func endpointsFromCluster(cmd *cobra.Command) []string { if !epClusterEndpoints { endpoints, err := cmd.Flags().GetStringSlice("endpoints") diff --git a/etcdctl/ctlv3/command/printer.go b/etcdctl/ctlv3/command/printer.go index b84dcda74..613c555eb 100644 --- a/etcdctl/ctlv3/command/printer.go +++ b/etcdctl/ctlv3/command/printer.go @@ -43,6 +43,7 @@ type printer interface { MemberList(v3.MemberListResponse) EndpointStatus([]epStatus) + EndpointHashKV([]epHashKV) MoveLeader(leader, target uint64, r v3.MoveLeaderResponse) Alarm(v3.AlarmResponse) @@ -146,6 +147,7 @@ func newPrinterUnsupported(n string) printer { } func (p *printerUnsupported) EndpointStatus([]epStatus) { p.p(nil) } +func (p *printerUnsupported) EndpointHashKV([]epHashKV) { p.p(nil) } func (p *printerUnsupported) DBStatus(dbstatus) { p.p(nil) } func (p *printerUnsupported) MoveLeader(leader, target uint64, r v3.MoveLeaderResponse) { p.p(nil) } @@ -184,6 +186,17 @@ func makeEndpointStatusTable(statusList []epStatus) (hdr []string, rows [][]stri return } +func makeEndpointHashKVTable(hashList []epHashKV) (hdr []string, rows [][]string) { + hdr = []string{"endpoint", "hash"} + for _, h := range hashList { + rows = append(rows, []string{ + h.Ep, + fmt.Sprint(h.Resp.Hash), + }) + } + return +} + func makeDBStatusTable(ds dbstatus) (hdr []string, rows [][]string) { hdr = []string{"hash", "revision", "total keys", "total size"} rows = append(rows, []string{ diff --git a/etcdctl/ctlv3/command/printer_fields.go b/etcdctl/ctlv3/command/printer_fields.go index 3e25a9504..f7d1cae59 100644 --- a/etcdctl/ctlv3/command/printer_fields.go +++ b/etcdctl/ctlv3/command/printer_fields.go @@ -146,6 +146,15 @@ func (p *fieldsPrinter) EndpointStatus(eps []epStatus) { } } +func (p *fieldsPrinter) EndpointHashKV(hs []epHashKV) { + for _, h := range hs { + p.hdr(h.Resp.Header) + fmt.Printf("\"Endpoint\" : %q\n", h.Ep) + fmt.Println(`"Hash" :`, h.Resp.Hash) + fmt.Println() + } +} + func (p *fieldsPrinter) Alarm(r v3.AlarmResponse) { p.hdr(r.Header) for _, a := range r.Alarms { diff --git a/etcdctl/ctlv3/command/printer_json.go b/etcdctl/ctlv3/command/printer_json.go index d5d884e59..19b3a5e68 100644 --- a/etcdctl/ctlv3/command/printer_json.go +++ b/etcdctl/ctlv3/command/printer_json.go @@ -29,6 +29,7 @@ func newJSONPrinter() printer { } func (p *jsonPrinter) EndpointStatus(r []epStatus) { printJSON(r) } +func (p *jsonPrinter) EndpointHashKV(r []epHashKV) { printJSON(r) } func (p *jsonPrinter) DBStatus(r dbstatus) { printJSON(r) } func printJSON(v interface{}) { diff --git a/etcdctl/ctlv3/command/printer_simple.go b/etcdctl/ctlv3/command/printer_simple.go index 00dda47f7..5e0ae9d3f 100644 --- a/etcdctl/ctlv3/command/printer_simple.go +++ b/etcdctl/ctlv3/command/printer_simple.go @@ -136,6 +136,13 @@ func (s *simplePrinter) EndpointStatus(statusList []epStatus) { } } +func (s *simplePrinter) EndpointHashKV(hashList []epHashKV) { + _, rows := makeEndpointHashKVTable(hashList) + for _, row := range rows { + fmt.Println(strings.Join(row, ", ")) + } +} + func (s *simplePrinter) DBStatus(ds dbstatus) { _, rows := makeDBStatusTable(ds) for _, row := range rows { diff --git a/etcdctl/ctlv3/command/printer_table.go b/etcdctl/ctlv3/command/printer_table.go index fb85c5846..1aea61a84 100644 --- a/etcdctl/ctlv3/command/printer_table.go +++ b/etcdctl/ctlv3/command/printer_table.go @@ -44,6 +44,16 @@ func (tp *tablePrinter) EndpointStatus(r []epStatus) { table.SetAlignment(tablewriter.ALIGN_RIGHT) table.Render() } +func (tp *tablePrinter) EndpointHashKV(r []epHashKV) { + hdr, rows := makeEndpointHashKVTable(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) DBStatus(r dbstatus) { hdr, rows := makeDBStatusTable(r) table := tablewriter.NewWriter(os.Stdout)