Browse Source

etcdctl/ctlv3: make 'table' printer configurable

Fix https://github.com/coreos/etcd/issues/5296.
Gyu-Ho Lee 9 years ago
parent
commit
3e088b3b40

+ 35 - 17
etcdctl/READMEv3.md

@@ -423,6 +423,18 @@ On success, prints a JSON listing of the member IDs, statuses, names, peer addre
 
 ```bash
 ./etcdctl member list
+8211f1d0f64f3269, started, infra1, http://127.0.0.1:12380, http://127.0.0.1:2379
+91bc3c398fb3c146, started, infra2, http://127.0.0.1:22380, http://127.0.0.1:22379
+fd422379fda50e48, started, infra3, http://127.0.0.1:32380, http://127.0.0.1:32379
+```
+
+```bash
+./etcdctl -w json member list
+{"header":{"cluster_id":17237436991929493444,"member_id":9372538179322589801,"raft_term":2},"members":[{"ID":9372538179322589801,"name":"infra1","peerURLs":["http://127.0.0.1:12380"],"clientURLs":["http://127.0.0.1:2379"]},{"ID":10501334649042878790,"name":"infra2","peerURLs":["http://127.0.0.1:22380"],"clientURLs":["http://127.0.0.1:22379"]},{"ID":18249187646912138824,"name":"infra3","peerURLs":["http://127.0.0.1:32380"],"clientURLs":["http://127.0.0.1:32379"]}]}
+```
+
+```bash
+./etcdctl -w table member list
 +------------------+---------+--------+------------------------+------------------------+
 |        ID        | STATUS  |  NAME  |       PEER ADDRS       |      CLIENT ADDRS      |
 +------------------+---------+--------+------------------------+------------------------+
@@ -432,11 +444,6 @@ On success, prints a JSON listing of the member IDs, statuses, names, peer addre
 +------------------+---------+--------+------------------------+------------------------+
 ```
 
-```bash
-./etcdctl -w json member list
-{"header":{"cluster_id":17237436991929493444,"member_id":9372538179322589801,"raft_term":2},"members":[{"ID":9372538179322589801,"name":"infra1","peerURLs":["http://127.0.0.1:12380"],"clientURLs":["http://127.0.0.1:2379"]},{"ID":10501334649042878790,"name":"infra2","peerURLs":["http://127.0.0.1:22380"],"clientURLs":["http://127.0.0.1:22379"]},{"ID":18249187646912138824,"name":"infra3","peerURLs":["http://127.0.0.1:32380"],"clientURLs":["http://127.0.0.1:32379"]}]}
-```
-
 ## Utility Commands
 
 ### ENDPOINT \<subcommand\>
@@ -481,13 +488,9 @@ On success, prints a line of JSON encoding each endpoint URL, ID, version, datab
 
 ```bash
 ./etcdctl endpoint status
-+-----------------+------------------+-----------+---------+-----------+-----------+------------+
-|    ENDPOINT     |        ID        |  VERSION  | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |
-+-----------------+------------------+-----------+---------+-----------+-----------+------------+
-| 127.0.0.1:2379  | 8211f1d0f64f3269 | 2.3.0+git | 25 kB   | false     |         2 |      31563 |
-| 127.0.0.1:22379 | 91bc3c398fb3c146 | 2.3.0+git | 25 kB   | false     |         2 |      31563 |
-| 127.0.0.1:32379 | fd422379fda50e48 | 2.3.0+git | 25 kB   | true      |         2 |      31563 |
-+-----------------+------------------+-----------+---------+-----------+-----------+------------+
+127.0.0.1:2379, 8211f1d0f64f3269, 3.0.0-beta.0+git, 25 kB, false, 2, 63
+127.0.0.1:22379, 91bc3c398fb3c146, 3.0.0-beta.0+git, 25 kB, false, 2, 63
+127.0.0.1:32379, fd422379fda50e48, 3.0.0-beta.0+git, 25 kB, true, 2, 63
 ```
 
 ```bash
@@ -495,6 +498,16 @@ On success, prints a line of JSON encoding each endpoint URL, ID, version, datab
 [{"Endpoint":"127.0.0.1:2379","Status":{"header":{"cluster_id":17237436991929493444,"member_id":9372538179322589801,"revision":2,"raft_term":2},"version":"2.3.0+git","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":"2.3.0+git","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":"2.3.0+git","dbSize":24576,"leader":18249187646912138824,"raftIndex":32623,"raftTerm":2}}]
 ```
 
+```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-beta.0+git | 25 kB   | false     |         2 |         52 |
+| 127.0.0.1:22379 | 91bc3c398fb3c146 | 3.0.0-beta.0+git | 25 kB   | false     |         2 |         52 |
+| 127.0.0.1:32379 | fd422379fda50e48 | 3.0.0-beta.0+git | 25 kB   | true      |         2 |         52 |
++-----------------+------------------+------------------+---------+-----------+-----------+------------+
+```
 
 ### LOCK \<lockname\>
 
@@ -705,11 +718,7 @@ On success, prints a line of JSON encoding the database hash, revision, total ke
 #### Examples
 ```bash
 ./etcdctl snapshot status file.db
-+----------+----------+------------+------------+
-|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
-+----------+----------+------------+------------+
-| cf1550fb |        3 |          3 | 25 kB      |
-+----------+----------+------------+------------+
+cf1550fb, 3, 3, 25 kB
 ```
 
 ```bash
@@ -717,6 +726,15 @@ On success, prints a line of JSON encoding the database hash, revision, total ke
 {"hash":3474280699,"revision":3,"totalKey":3,"totalSize":24576}
 ```
 
+```bash
+./etcdctl -write-out=table snapshot status file.db
++----------+----------+------------+------------+
+|   HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
++----------+----------+------------+------------+
+| cf1550fb |        3 |          3 | 25 kB      |
++----------+----------+------------+------------+
+```
+
 ## Notes
 
 - JSON encoding for keys and values uses base64 since they are byte strings.

+ 4 - 1
etcdctl/ctlv3/command/ep_command.go

@@ -51,7 +51,10 @@ func newEpStatusCommand() *cobra.Command {
 	return &cobra.Command{
 		Use:   "status",
 		Short: "status prints out the status of endpoints specified in `--endpoints` flag",
-		Run:   epStatusCommandFunc,
+		Long: `When --write-out is set to simple, this command prints out comma-separated status lists for each endpoint.
+The items in the lists are endpoint, ID, version, db size, is leader, raft term, raft index.
+`,
+		Run: epStatusCommandFunc,
 	}
 }
 

+ 3 - 0
etcdctl/ctlv3/command/member_command.go

@@ -84,6 +84,9 @@ func NewMemberListCommand() *cobra.Command {
 	cc := &cobra.Command{
 		Use:   "list",
 		Short: "list is used to list all members in the cluster",
+		Long: `When --write-out is set to simple, this command prints out comma-separated member lists for each endpoint.
+The items in the lists are ID, Status, Name, Peer Addrs, Client Addrs.
+`,
 
 		Run: memberListCommandFunc,
 	}

+ 101 - 40
etcdctl/ctlv3/command/printer.go

@@ -51,10 +51,57 @@ func NewPrinter(printerType string, isHex bool) printer {
 		return &jsonPrinter{}
 	case "protobuf":
 		return &pbPrinter{}
+	case "table":
+		return &tablePrinter{}
 	}
 	return nil
 }
 
+func makeMemberListTable(r v3.MemberListResponse) (hdr []string, rows [][]string) {
+	hdr = []string{"ID", "Status", "Name", "Peer Addrs", "Client Addrs"}
+	for _, m := range r.Members {
+		status := "started"
+		if len(m.Name) == 0 {
+			status = "unstarted"
+		}
+		rows = append(rows, []string{
+			fmt.Sprintf("%x", m.ID),
+			status,
+			m.Name,
+			strings.Join(m.PeerURLs, ","),
+			strings.Join(m.ClientURLs, ","),
+		})
+	}
+	return
+}
+
+func makeEndpointStatusTable(statusList []epStatus) (hdr []string, rows [][]string) {
+	hdr = []string{"endpoint", "ID", "version", "db size", "is leader", "raft term", "raft index"}
+	for _, status := range statusList {
+		rows = append(rows, []string{
+			fmt.Sprint(status.Ep),
+			fmt.Sprintf("%x", status.Resp.Header.MemberId),
+			fmt.Sprint(status.Resp.Version),
+			fmt.Sprint(humanize.Bytes(uint64(status.Resp.DbSize))),
+			fmt.Sprint(status.Resp.Leader == status.Resp.Header.MemberId),
+			fmt.Sprint(status.Resp.RaftTerm),
+			fmt.Sprint(status.Resp.RaftIndex),
+		})
+	}
+	return
+}
+
+func makeDBStatusTable(ds dbstatus) (hdr []string, rows [][]string) {
+	hdr = []string{"hash", "revision", "total keys", "total size"}
+	rows = append(rows, []string{
+		fmt.Sprintf("%x", ds.Hash),
+		fmt.Sprint(ds.Revision),
+		fmt.Sprint(ds.TotalKey),
+		humanize.Bytes(uint64(ds.TotalSize)),
+	})
+	return
+}
+
 type simplePrinter struct {
 	isHex bool
 }
@@ -107,57 +154,71 @@ func (s *simplePrinter) Alarm(resp v3.AlarmResponse) {
 }
 
 func (s *simplePrinter) MemberList(resp v3.MemberListResponse) {
-	table := tablewriter.NewWriter(os.Stdout)
-	table.SetHeader([]string{"ID", "Status", "Name", "Peer Addrs", "Client Addrs"})
+	_, rows := makeMemberListTable(resp)
+	for _, row := range rows {
+		fmt.Println(strings.Join(row, ", "))
+	}
+}
 
-	for _, m := range resp.Members {
-		status := "started"
-		if len(m.Name) == 0 {
-			status = "unstarted"
-		}
+func (s *simplePrinter) EndpointStatus(statusList []epStatus) {
+	_, rows := makeEndpointStatusTable(statusList)
+	for _, row := range rows {
+		fmt.Println(strings.Join(row, ", "))
+	}
+}
 
-		table.Append([]string{
-			fmt.Sprintf("%x", m.ID),
-			status,
-			m.Name,
-			strings.Join(m.PeerURLs, ","),
-			strings.Join(m.ClientURLs, ","),
-		})
+func (s *simplePrinter) DBStatus(ds dbstatus) {
+	_, rows := makeDBStatusTable(ds)
+	for _, row := range rows {
+		fmt.Println(strings.Join(row, ", "))
 	}
+}
 
+type tablePrinter struct{}
+
+func (tp *tablePrinter) Del(r v3.DeleteResponse) {
+	ExitWithError(ExitBadFeature, errors.New("table is not supported as output format"))
+}
+func (tp *tablePrinter) Get(r v3.GetResponse) {
+	ExitWithError(ExitBadFeature, errors.New("table is not supported as output format"))
+}
+func (tp *tablePrinter) Put(r v3.PutResponse) {
+	ExitWithError(ExitBadFeature, errors.New("table is not supported as output format"))
+}
+func (tp *tablePrinter) Txn(r v3.TxnResponse) {
+	ExitWithError(ExitBadFeature, errors.New("table is not supported as output format"))
+}
+func (tp *tablePrinter) Watch(r v3.WatchResponse) {
+	ExitWithError(ExitBadFeature, errors.New("table is not supported as output format"))
+}
+func (tp *tablePrinter) Alarm(r v3.AlarmResponse) {
+	ExitWithError(ExitBadFeature, errors.New("table is not supported as output format"))
+}
+func (tp *tablePrinter) MemberList(r v3.MemberListResponse) {
+	hdr, rows := makeMemberListTable(r)
+	table := tablewriter.NewWriter(os.Stdout)
+	table.SetHeader(hdr)
+	for _, row := range rows {
+		table.Append(row)
+	}
 	table.Render()
 }
-
-func (s *simplePrinter) EndpointStatus(statusList []epStatus) {
+func (tp *tablePrinter) EndpointStatus(r []epStatus) {
+	hdr, rows := makeEndpointStatusTable(r)
 	table := tablewriter.NewWriter(os.Stdout)
-	table.SetHeader([]string{"endpoint", "ID", "version", "db size", "is leader", "raft term", "raft index"})
-
-	for _, status := range statusList {
-		table.Append([]string{
-			fmt.Sprint(status.Ep),
-			fmt.Sprintf("%x", status.Resp.Header.MemberId),
-			fmt.Sprint(status.Resp.Version),
-			fmt.Sprint(humanize.Bytes(uint64(status.Resp.DbSize))),
-			fmt.Sprint(status.Resp.Leader == status.Resp.Header.MemberId),
-			fmt.Sprint(status.Resp.RaftTerm),
-			fmt.Sprint(status.Resp.RaftIndex),
-		})
+	table.SetHeader(hdr)
+	for _, row := range rows {
+		table.Append(row)
 	}
-
 	table.Render()
 }
-
-func (s *simplePrinter) DBStatus(ds dbstatus) {
+func (tp *tablePrinter) DBStatus(r dbstatus) {
+	hdr, rows := makeDBStatusTable(r)
 	table := tablewriter.NewWriter(os.Stdout)
-	table.SetHeader([]string{"hash", "revision", "total keys", "total size"})
-
-	table.Append([]string{
-		fmt.Sprintf("%x", ds.Hash),
-		fmt.Sprint(ds.Revision),
-		fmt.Sprint(ds.TotalKey),
-		humanize.Bytes(uint64(ds.TotalSize)),
-	})
-
+	table.SetHeader(hdr)
+	for _, row := range rows {
+		table.Append(row)
+	}
 	table.Render()
 }
 

+ 4 - 1
etcdctl/ctlv3/command/snapshot_command.go

@@ -76,7 +76,10 @@ func newSnapshotStatusCommand() *cobra.Command {
 	return &cobra.Command{
 		Use:   "status <filename>",
 		Short: "status gets backend snapshot status of a given file.",
-		Run:   snapshotStatusCommandFunc,
+		Long: `When --write-out is set to simple, this command prints out comma-separated status lists for each endpoint.
+The items in the lists are hash, revision, total keys, total size.
+`,
+		Run: snapshotStatusCommandFunc,
 	}
 }
 

+ 1 - 1
etcdctl/ctlv3/ctl.go

@@ -45,7 +45,7 @@ var (
 func init() {
 	rootCmd.PersistentFlags().StringSliceVar(&globalFlags.Endpoints, "endpoints", []string{"127.0.0.1:2379", "127.0.0.1:22379", "127.0.0.1:32379"}, "gRPC endpoints")
 
-	rootCmd.PersistentFlags().StringVarP(&globalFlags.OutputFormat, "write-out", "w", "simple", "set the output format (simple, json, protobuf)")
+	rootCmd.PersistentFlags().StringVarP(&globalFlags.OutputFormat, "write-out", "w", "simple", "set the output format (simple, json, protobuf, table)")
 	rootCmd.PersistentFlags().BoolVar(&globalFlags.IsHex, "hex", false, "print byte strings as hex encoded strings")
 
 	rootCmd.PersistentFlags().DurationVar(&globalFlags.DialTimeout, "dial-timeout", defaultDialTimeout, "dial timeout for client connections")