Browse Source

etcdctl: --cluster flag using cluster endpoints for endpoint commands

Queries the cluster for endpoints to use for the endpoint commands.

Fixes #8117
Anthony Romano 8 years ago
parent
commit
1a2be432c5
2 changed files with 62 additions and 19 deletions
  1. 30 13
      etcdctl/README.md
  2. 32 6
      etcdctl/ctlv3/command/ep_command.go

+ 30 - 13
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 \<subcommand\>

+ 32 - 6
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
+}