diff --git a/etcdctl/README.md b/etcdctl/README.md index 22682370a..84ad1dce2 100644 --- a/etcdctl/README.md +++ b/etcdctl/README.md @@ -565,6 +565,10 @@ Prints a humanized table of the member IDs, statuses, names, peer addresses, and ENDPOINT provides commands for querying individual endpoints. +#### Options + +- cluster -- fetch and use all endpoints from the etcd cluster member list + ### ENDPOINT HEALTH ENDPOINT HEALTH checks the health of the list of endpoints with respect to cluster. An endpoint is unhealthy @@ -576,11 +580,20 @@ If an endpoint can participate in consensus, prints a message indicating the end #### Example +Check the default endpoint's health: + ```bash ./etcdctl endpoint health -# 127.0.0.1:32379 is healthy: successfully committed proposal: took = 2.130877ms # 127.0.0.1:2379 is healthy: successfully committed proposal: took = 2.095242ms -# 127.0.0.1:22379 is healthy: successfully committed proposal: took = 2.083263ms +``` + +Check all endpoints for the cluster associated with the default endpoint: + +```bash +./etcdctl endpoint --cluster health +# http://127.0.0.1:2379 is healthy: successfully committed proposal: took = 1.060091ms +# http://127.0.0.1:22379 is healthy: successfully committed proposal: took = 903.138µs +# http://127.0.0.1:32379 is healthy: successfully committed proposal: took = 1.113848ms ``` ### ENDPOINT STATUS @@ -599,27 +612,31 @@ Prints a line of JSON encoding each endpoint URL, ID, version, database size, le #### Examples +Get the status for the default endpoint: + ```bash ./etcdctl endpoint status # 127.0.0.1:2379, 8211f1d0f64f3269, 3.0.0, 25 kB, false, 2, 63 -# 127.0.0.1:22379, 91bc3c398fb3c146, 3.0.0, 25 kB, false, 2, 63 -# 127.0.0.1:32379, fd422379fda50e48, 3.0.0, 25 kB, true, 2, 63 ``` +Get the status for the default endpoint as JSON: + ```bash ./etcdctl -w json endpoint status -# [{"Endpoint":"127.0.0.1:2379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":9372538179322589801,"revision":2,"raft_term":2},"version":"3.0.0","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}},{"Endpoint":"127.0.0.1:22379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":10501334649042878790,"revision":2,"raft_term":2},"version":"3.0.0","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}},{"Endpoint":"127.0.0.1:32379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":18249187646912138824,"revision":2,"raft_term":2},"version":"3.0.0","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}}] +# [{"Endpoint":"127.0.0.1:2379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":9372538179322589801,"revision":2,"raft_term":2},"version":"3.0.0","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}}] ``` +Get the status for all endpoints in the cluster associated with the default endpoint: + ```bash -./etcdctl -w table endpoint status -+-----------------+------------------+---------+---------+-----------+-----------+------------+ -| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX | -+-----------------+------------------+---------+---------+-----------+-----------+------------+ -| 127.0.0.1:2379 | 8211f1d0f64f3269 | 3.0.0 | 25 kB | false | 2 | 52 | -| 127.0.0.1:22379 | 91bc3c398fb3c146 | 3.0.0 | 25 kB | false | 2 | 52 | -| 127.0.0.1:32379 | fd422379fda50e48 | 3.0.0 | 25 kB | true | 2 | 52 | -+-----------------+------------------+---------+---------+-----------+-----------+------------+ +./etcdctl -w table endpoint --cluster status ++------------------------+------------------+----------------+---------+-----------+-----------+------------+ +| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX | ++------------------------+------------------+----------------+---------+-----------+-----------+------------+ +| http://127.0.0.1:2379 | 8211f1d0f64f3269 | 3.2.0-rc.1+git | 25 kB | false | 2 | 8 | +| http://127.0.0.1:22379 | 91bc3c398fb3c146 | 3.2.0-rc.1+git | 25 kB | false | 2 | 8 | +| http://127.0.0.1:32379 | fd422379fda50e48 | 3.2.0-rc.1+git | 25 kB | true | 2 | 8 | ++------------------------+------------------+----------------+---------+-----------+-----------+------------+ ``` ### ALARM \ diff --git a/etcdctl/ctlv3/command/ep_command.go b/etcdctl/ctlv3/command/ep_command.go index dab6d20df..329875cd7 100644 --- a/etcdctl/ctlv3/command/ep_command.go +++ b/etcdctl/ctlv3/command/ep_command.go @@ -27,6 +27,8 @@ import ( "github.com/spf13/cobra" ) +var epClusterEndpoints bool + // NewEndpointCommand returns the cobra command for "endpoint". func NewEndpointCommand() *cobra.Command { ec := &cobra.Command{ @@ -34,6 +36,7 @@ func NewEndpointCommand() *cobra.Command { Short: "Endpoint related commands", } + ec.PersistentFlags().BoolVar(&epClusterEndpoints, "cluster", false, "use all endpoints from the cluster member list") ec.AddCommand(newEpHealthCommand()) ec.AddCommand(newEpStatusCommand()) @@ -64,16 +67,12 @@ The items in the lists are endpoint, ID, version, db size, is leader, raft term, // epHealthCommandFunc executes the "endpoint-health" command. func epHealthCommandFunc(cmd *cobra.Command, args []string) { flags.SetPflagsFromEnv("ETCDCTL", cmd.InheritedFlags()) - endpoints, err := cmd.Flags().GetStringSlice("endpoints") - if err != nil { - ExitWithError(ExitError, err) - } sec := secureCfgFromCmd(cmd) dt := dialTimeoutFromCmd(cmd) auth := authCfgFromCmd(cmd) cfgs := []*v3.Config{} - for _, ep := range endpoints { + for _, ep := range endpointsFromCluster(cmd) { cfg, err := newClientCfg([]string{ep}, dt, sec, auth) if err != nil { ExitWithError(ExitBadArgs, err) @@ -121,7 +120,7 @@ func epStatusCommandFunc(cmd *cobra.Command, args []string) { statusList := []epStatus{} var err error - for _, ep := range c.Endpoints() { + for _, ep := range endpointsFromCluster(cmd) { ctx, cancel := commandCtx(cmd) resp, serr := c.Status(ctx, ep) cancel() @@ -139,3 +138,30 @@ func epStatusCommandFunc(cmd *cobra.Command, args []string) { os.Exit(ExitError) } } + +func endpointsFromCluster(cmd *cobra.Command) []string { + if !epClusterEndpoints { + endpoints, err := cmd.Flags().GetStringSlice("endpoints") + if err != nil { + ExitWithError(ExitError, err) + } + return endpoints + } + c := mustClientFromCmd(cmd) + ctx, cancel := commandCtx(cmd) + defer func() { + c.Close() + cancel() + }() + membs, err := c.MemberList(ctx) + if err != nil { + err = fmt.Errorf("failed to fetch endpoints from etcd cluster member list: %v", err) + ExitWithError(ExitError, err) + } + + ret := []string{} + for _, m := range membs.Members { + ret = append(ret, m.ClientURLs...) + } + return ret +}