Merge pull request #7001 from heyitsanthony/etcdctl-doc

etcdctl: tighten up output, reorganize README.md
This commit is contained in:
Anthony Romano 2017-01-04 13:44:49 -08:00 committed by GitHub
commit 154f268031
10 changed files with 1201 additions and 1033 deletions

File diff suppressed because it is too large Load Diff

View File

@ -112,7 +112,7 @@ func memberAddCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitError, err)
}
fmt.Printf("Member %16x added to cluster %16x\n", resp.Member.ID, resp.Header.ClusterId)
display.MemberAdd(*resp)
}
// memberRemoveCommandFunc executes the "member remove" command.
@ -132,8 +132,7 @@ func memberRemoveCommandFunc(cmd *cobra.Command, args []string) {
if err != nil {
ExitWithError(ExitError, err)
}
fmt.Printf("Member %16x removed from cluster %16x\n", id, resp.Header.ClusterId)
display.MemberRemove(id, *resp)
}
// memberUpdateCommandFunc executes the "member update" command.
@ -160,7 +159,7 @@ func memberUpdateCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitError, err)
}
fmt.Printf("Member %16x updated in cluster %16x\n", id, resp.Header.ClusterId)
display.MemberUpdate(id, *resp)
}
// memberListCommandFunc executes the "member list" command.

View File

@ -15,17 +15,14 @@
package command
import (
"encoding/json"
"errors"
"fmt"
"os"
"strings"
v3 "github.com/coreos/etcd/clientv3"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
spb "github.com/coreos/etcd/mvcc/mvccpb"
"github.com/dustin/go-humanize"
"github.com/olekukonko/tablewriter"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
)
type printer interface {
@ -37,12 +34,30 @@ type printer interface {
TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool)
MemberAdd(v3.MemberAddResponse)
MemberRemove(id uint64, r v3.MemberRemoveResponse)
MemberUpdate(id uint64, r v3.MemberUpdateResponse)
MemberList(v3.MemberListResponse)
EndpointStatus([]epStatus)
Alarm(v3.AlarmResponse)
DBStatus(dbstatus)
RoleAdd(role string, r v3.AuthRoleAddResponse)
RoleGet(role string, r v3.AuthRoleGetResponse)
RoleDelete(role string, r v3.AuthRoleDeleteResponse)
RoleList(v3.AuthRoleListResponse)
RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse)
RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse)
UserAdd(user string, r v3.AuthUserAddResponse)
UserGet(user string, r v3.AuthUserGetResponse)
UserList(r v3.AuthUserListResponse)
UserChangePassword(v3.AuthUserChangePasswordResponse)
UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse)
UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse)
UserDelete(user string, r v3.AuthUserDeleteResponse)
}
func NewPrinter(printerType string, isHex bool) printer {
@ -50,17 +65,78 @@ func NewPrinter(printerType string, isHex bool) printer {
case "simple":
return &simplePrinter{isHex: isHex}
case "fields":
return &fieldsPrinter{}
return &fieldsPrinter{newPrinterUnsupported("fields")}
case "json":
return &jsonPrinter{}
return newJSONPrinter()
case "protobuf":
return &pbPrinter{}
return newPBPrinter()
case "table":
return &tablePrinter{}
return &tablePrinter{newPrinterUnsupported("table")}
}
return nil
}
type printerRPC struct {
printer
p func(interface{})
}
func (p *printerRPC) Del(r v3.DeleteResponse) { p.p((*pb.DeleteRangeResponse)(&r)) }
func (p *printerRPC) Get(r v3.GetResponse) { p.p((*pb.RangeResponse)(&r)) }
func (p *printerRPC) Put(r v3.PutResponse) { p.p((*pb.PutResponse)(&r)) }
func (p *printerRPC) Txn(r v3.TxnResponse) { p.p((*pb.TxnResponse)(&r)) }
func (p *printerRPC) Watch(r v3.WatchResponse) { p.p(&r) }
func (p *printerRPC) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) { p.p(&r) }
func (p *printerRPC) MemberAdd(r v3.MemberAddResponse) { p.p((*pb.MemberAddResponse)(&r)) }
func (p *printerRPC) MemberRemove(id uint64, r v3.MemberRemoveResponse) {
p.p((*pb.MemberRemoveResponse)(&r))
}
func (p *printerRPC) MemberUpdate(id uint64, r v3.MemberUpdateResponse) {
p.p((*pb.MemberUpdateResponse)(&r))
}
func (p *printerRPC) MemberList(r v3.MemberListResponse) { p.p((*pb.MemberListResponse)(&r)) }
func (p *printerRPC) Alarm(r v3.AlarmResponse) { p.p((*pb.AlarmResponse)(&r)) }
func (p *printerRPC) RoleAdd(_ string, r v3.AuthRoleAddResponse) { p.p((*pb.AuthRoleAddResponse)(&r)) }
func (p *printerRPC) RoleGet(_ string, r v3.AuthRoleGetResponse) { p.p((*pb.AuthRoleGetResponse)(&r)) }
func (p *printerRPC) RoleDelete(_ string, r v3.AuthRoleDeleteResponse) {
p.p((*pb.AuthRoleDeleteResponse)(&r))
}
func (p *printerRPC) RoleList(r v3.AuthRoleListResponse) { p.p((*pb.AuthRoleListResponse)(&r)) }
func (p *printerRPC) RoleGrantPermission(_ string, r v3.AuthRoleGrantPermissionResponse) {
p.p((*pb.AuthRoleGrantPermissionResponse)(&r))
}
func (p *printerRPC) RoleRevokePermission(_ string, _ string, _ string, r v3.AuthRoleRevokePermissionResponse) {
p.p((*pb.AuthRoleRevokePermissionResponse)(&r))
}
func (p *printerRPC) UserAdd(_ string, r v3.AuthUserAddResponse) { p.p((*pb.AuthUserAddResponse)(&r)) }
func (p *printerRPC) UserGet(_ string, r v3.AuthUserGetResponse) { p.p((*pb.AuthUserGetResponse)(&r)) }
func (p *printerRPC) UserList(r v3.AuthUserListResponse) { p.p((*pb.AuthUserListResponse)(&r)) }
func (p *printerRPC) UserChangePassword(r v3.AuthUserChangePasswordResponse) {
p.p((*pb.AuthUserChangePasswordResponse)(&r))
}
func (p *printerRPC) UserGrantRole(_ string, _ string, r v3.AuthUserGrantRoleResponse) {
p.p((*pb.AuthUserGrantRoleResponse)(&r))
}
func (p *printerRPC) UserRevokeRole(_ string, _ string, r v3.AuthUserRevokeRoleResponse) {
p.p((*pb.AuthUserRevokeRoleResponse)(&r))
}
func (p *printerRPC) UserDelete(_ string, r v3.AuthUserDeleteResponse) {
p.p((*pb.AuthUserDeleteResponse)(&r))
}
type printerUnsupported struct{ printerRPC }
func newPrinterUnsupported(n string) printer {
f := func(interface{}) {
ExitWithError(ExitBadFeature, errors.New(n+" not supported as output format"))
}
return &printerUnsupported{printerRPC{nil, f}}
}
func (p *printerUnsupported) EndpointStatus([]epStatus) { p.p(nil) }
func (p *printerUnsupported) DBStatus(dbstatus) { p.p(nil) }
func makeMemberListTable(r v3.MemberListResponse) (hdr []string, rows [][]string) {
hdr = []string{"ID", "Status", "Name", "Peer Addrs", "Client Addrs"}
for _, m := range r.Members {
@ -105,353 +181,3 @@ func makeDBStatusTable(ds dbstatus) (hdr []string, rows [][]string) {
})
return
}
type simplePrinter struct {
isHex bool
valueOnly bool
}
func (s *simplePrinter) Del(resp v3.DeleteResponse) {
fmt.Println(resp.Deleted)
for _, kv := range resp.PrevKvs {
printKV(s.isHex, s.valueOnly, kv)
}
}
func (s *simplePrinter) Get(resp v3.GetResponse) {
for _, kv := range resp.Kvs {
printKV(s.isHex, s.valueOnly, kv)
}
}
func (s *simplePrinter) Put(r v3.PutResponse) {
fmt.Println("OK")
if r.PrevKv != nil {
printKV(s.isHex, s.valueOnly, r.PrevKv)
}
}
func (s *simplePrinter) Txn(resp v3.TxnResponse) {
if resp.Succeeded {
fmt.Println("SUCCESS")
} else {
fmt.Println("FAILURE")
}
for _, r := range resp.Responses {
fmt.Println("")
switch v := r.Response.(type) {
case *pb.ResponseOp_ResponseDeleteRange:
s.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
case *pb.ResponseOp_ResponsePut:
s.Put((v3.PutResponse)(*v.ResponsePut))
case *pb.ResponseOp_ResponseRange:
s.Get(((v3.GetResponse)(*v.ResponseRange)))
default:
fmt.Printf("unexpected response %+v\n", r)
}
}
}
func (s *simplePrinter) Watch(resp v3.WatchResponse) {
for _, e := range resp.Events {
fmt.Println(e.Type)
if e.PrevKv != nil {
printKV(s.isHex, s.valueOnly, e.PrevKv)
}
printKV(s.isHex, s.valueOnly, e.Kv)
}
}
func (s *simplePrinter) TimeToLive(resp v3.LeaseTimeToLiveResponse, keys bool) {
txt := fmt.Sprintf("lease %016x granted with TTL(%ds), remaining(%ds)", resp.ID, resp.GrantedTTL, resp.TTL)
if keys {
ks := make([]string, len(resp.Keys))
for i := range resp.Keys {
ks[i] = string(resp.Keys[i])
}
txt += fmt.Sprintf(", attached keys(%v)", ks)
}
fmt.Println(txt)
}
func (s *simplePrinter) Alarm(resp v3.AlarmResponse) {
for _, e := range resp.Alarms {
fmt.Printf("%+v\n", e)
}
}
func (s *simplePrinter) MemberList(resp v3.MemberListResponse) {
_, rows := makeMemberListTable(resp)
for _, row := range rows {
fmt.Println(strings.Join(row, ", "))
}
}
func (s *simplePrinter) EndpointStatus(statusList []epStatus) {
_, rows := makeEndpointStatusTable(statusList)
for _, row := range rows {
fmt.Println(strings.Join(row, ", "))
}
}
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) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
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 (tp *tablePrinter) EndpointStatus(r []epStatus) {
hdr, rows := makeEndpointStatusTable(r)
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(hdr)
for _, row := range rows {
table.Append(row)
}
table.Render()
}
func (tp *tablePrinter) DBStatus(r dbstatus) {
hdr, rows := makeDBStatusTable(r)
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(hdr)
for _, row := range rows {
table.Append(row)
}
table.Render()
}
type jsonPrinter struct{}
func (p *jsonPrinter) Del(r v3.DeleteResponse) { printJSON(r) }
func (p *jsonPrinter) Get(r v3.GetResponse) { printJSON(r) }
func (p *jsonPrinter) Put(r v3.PutResponse) { printJSON(r) }
func (p *jsonPrinter) Txn(r v3.TxnResponse) { printJSON(r) }
func (p *jsonPrinter) Watch(r v3.WatchResponse) { printJSON(r) }
func (p *jsonPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) { printJSON(r) }
func (p *jsonPrinter) Alarm(r v3.AlarmResponse) { printJSON(r) }
func (p *jsonPrinter) MemberList(r v3.MemberListResponse) { printJSON(r) }
func (p *jsonPrinter) EndpointStatus(r []epStatus) { printJSON(r) }
func (p *jsonPrinter) DBStatus(r dbstatus) { printJSON(r) }
func printJSON(v interface{}) {
b, err := json.Marshal(v)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return
}
fmt.Println(string(b))
}
type pbPrinter struct{}
type pbMarshal interface {
Marshal() ([]byte, error)
}
func (p *pbPrinter) Del(r v3.DeleteResponse) {
printPB((*pb.DeleteRangeResponse)(&r))
}
func (p *pbPrinter) Get(r v3.GetResponse) {
printPB((*pb.RangeResponse)(&r))
}
func (p *pbPrinter) Put(r v3.PutResponse) {
printPB((*pb.PutResponse)(&r))
}
func (p *pbPrinter) Txn(r v3.TxnResponse) {
printPB((*pb.TxnResponse)(&r))
}
func (p *pbPrinter) Watch(r v3.WatchResponse) {
for _, ev := range r.Events {
printPB((*spb.Event)(ev))
}
}
func (p *pbPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
ExitWithError(ExitBadFeature, errors.New("only support simple or json as output format"))
}
func (p *pbPrinter) Alarm(r v3.AlarmResponse) {
printPB((*pb.AlarmResponse)(&r))
}
func (p *pbPrinter) MemberList(r v3.MemberListResponse) {
printPB((*pb.MemberListResponse)(&r))
}
func (p *pbPrinter) EndpointStatus(statusList []epStatus) {
ExitWithError(ExitBadFeature, errors.New("only support simple or json as output format"))
}
func (p *pbPrinter) DBStatus(r dbstatus) {
ExitWithError(ExitBadFeature, errors.New("only support simple or json as output format"))
}
func printPB(m pbMarshal) {
b, err := m.Marshal()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return
}
fmt.Printf(string(b))
}
type fieldsPrinter struct{}
func (p *fieldsPrinter) kv(pfx string, kv *spb.KeyValue) {
fmt.Printf("\"%sKey\" : %q\n", pfx, string(kv.Key))
fmt.Printf("\"%sCreateRevision\" : %d\n", pfx, kv.CreateRevision)
fmt.Printf("\"%sModRevision\" : %d\n", pfx, kv.ModRevision)
fmt.Printf("\"%sVersion\" : %d\n", pfx, kv.Version)
fmt.Printf("\"%sValue\" : %q\n", pfx, string(kv.Value))
fmt.Printf("\"%sLease\" : %d\n", pfx, string(kv.Lease))
}
func (p *fieldsPrinter) hdr(h *pb.ResponseHeader) {
fmt.Println(`"ClusterID" :`, h.ClusterId)
fmt.Println(`"MemberID" :`, h.MemberId)
fmt.Println(`"Revision" :`, h.Revision)
fmt.Println(`"RaftTerm" :`, h.RaftTerm)
}
func (p *fieldsPrinter) Del(r v3.DeleteResponse) {
p.hdr(r.Header)
fmt.Println(`"Deleted" :`, r.Deleted)
for _, kv := range r.PrevKvs {
p.kv("Prev", kv)
}
}
func (p *fieldsPrinter) Get(r v3.GetResponse) {
p.hdr(r.Header)
for _, kv := range r.Kvs {
p.kv("", kv)
}
fmt.Println(`"More" :`, r.More)
fmt.Println(`"Count" :`, r.Count)
}
func (p *fieldsPrinter) Put(r v3.PutResponse) {
p.hdr(r.Header)
if r.PrevKv != nil {
p.kv("Prev", r.PrevKv)
}
}
func (p *fieldsPrinter) Txn(r v3.TxnResponse) {
p.hdr(r.Header)
fmt.Println(`"Succeeded" :`, r.Succeeded)
for _, resp := range r.Responses {
switch v := resp.Response.(type) {
case *pb.ResponseOp_ResponseDeleteRange:
p.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
case *pb.ResponseOp_ResponsePut:
p.Put((v3.PutResponse)(*v.ResponsePut))
case *pb.ResponseOp_ResponseRange:
p.Get((v3.GetResponse)(*v.ResponseRange))
default:
fmt.Printf("\"Unknown\" : %q\n", fmt.Sprintf("%+v", v))
}
}
}
func (p *fieldsPrinter) Watch(resp v3.WatchResponse) {
p.hdr(&resp.Header)
for _, e := range resp.Events {
fmt.Println(`"Type" : `, e.Type)
if e.PrevKv != nil {
p.kv("Prev", e.PrevKv)
}
p.kv("", e.Kv)
}
}
func (p *fieldsPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
p.hdr(r.ResponseHeader)
fmt.Println(`"ID" :`, r.ID)
fmt.Println(`"TTL" :`, r.TTL)
fmt.Println(`"GrantedTTL" :`, r.GrantedTTL)
for _, k := range r.Keys {
fmt.Printf("\"Key\" : %q\n", string(k))
}
}
func (p *fieldsPrinter) MemberList(r v3.MemberListResponse) {
p.hdr(r.Header)
for _, m := range r.Members {
fmt.Println(`"ID" :`, m.ID)
fmt.Printf("\"Name\" : %q\n", m.Name)
for _, u := range m.PeerURLs {
fmt.Printf("\"PeerURL\" : %q\n", u)
}
for _, u := range m.ClientURLs {
fmt.Printf("\"ClientURL\" : %q\n", u)
}
fmt.Println()
}
}
func (p *fieldsPrinter) EndpointStatus(eps []epStatus) {
for _, ep := range eps {
p.hdr(ep.Resp.Header)
fmt.Printf("\"Version\" : %q\n", ep.Resp.Version)
fmt.Println(`"DBSize" :"`, ep.Resp.DbSize)
fmt.Println(`"Leader" :"`, ep.Resp.Leader)
fmt.Println(`"RaftIndex" :"`, ep.Resp.RaftIndex)
fmt.Println(`"RaftTerm" :"`, ep.Resp.RaftTerm)
fmt.Printf("\"Endpoint\" : %q\n", ep.Ep)
fmt.Println()
}
}
func (p *fieldsPrinter) Alarm(r v3.AlarmResponse) {
p.hdr(r.Header)
for _, a := range r.Alarms {
fmt.Println(`"MemberID" :`, a.MemberID)
fmt.Println(`"AlarmType" :`, a.Alarm)
fmt.Println()
}
}
func (p *fieldsPrinter) DBStatus(r dbstatus) {
fmt.Println(`"Hash" :`, r.Hash)
fmt.Println(`"Revision" :`, r.Revision)
fmt.Println(`"Keys" :`, r.TotalKey)
fmt.Println(`"Size" :`, r.TotalSize)
}

View File

@ -0,0 +1,167 @@
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command
import (
"fmt"
v3 "github.com/coreos/etcd/clientv3"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
spb "github.com/coreos/etcd/mvcc/mvccpb"
)
type fieldsPrinter struct{ printer }
func (p *fieldsPrinter) kv(pfx string, kv *spb.KeyValue) {
fmt.Printf("\"%sKey\" : %q\n", pfx, string(kv.Key))
fmt.Printf("\"%sCreateRevision\" : %d\n", pfx, kv.CreateRevision)
fmt.Printf("\"%sModRevision\" : %d\n", pfx, kv.ModRevision)
fmt.Printf("\"%sVersion\" : %d\n", pfx, kv.Version)
fmt.Printf("\"%sValue\" : %q\n", pfx, string(kv.Value))
fmt.Printf("\"%sLease\" : %d\n", pfx, string(kv.Lease))
}
func (p *fieldsPrinter) hdr(h *pb.ResponseHeader) {
fmt.Println(`"ClusterID" :`, h.ClusterId)
fmt.Println(`"MemberID" :`, h.MemberId)
fmt.Println(`"Revision" :`, h.Revision)
fmt.Println(`"RaftTerm" :`, h.RaftTerm)
}
func (p *fieldsPrinter) Del(r v3.DeleteResponse) {
p.hdr(r.Header)
fmt.Println(`"Deleted" :`, r.Deleted)
for _, kv := range r.PrevKvs {
p.kv("Prev", kv)
}
}
func (p *fieldsPrinter) Get(r v3.GetResponse) {
p.hdr(r.Header)
for _, kv := range r.Kvs {
p.kv("", kv)
}
fmt.Println(`"More" :`, r.More)
fmt.Println(`"Count" :`, r.Count)
}
func (p *fieldsPrinter) Put(r v3.PutResponse) {
p.hdr(r.Header)
if r.PrevKv != nil {
p.kv("Prev", r.PrevKv)
}
}
func (p *fieldsPrinter) Txn(r v3.TxnResponse) {
p.hdr(r.Header)
fmt.Println(`"Succeeded" :`, r.Succeeded)
for _, resp := range r.Responses {
switch v := resp.Response.(type) {
case *pb.ResponseOp_ResponseDeleteRange:
p.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
case *pb.ResponseOp_ResponsePut:
p.Put((v3.PutResponse)(*v.ResponsePut))
case *pb.ResponseOp_ResponseRange:
p.Get((v3.GetResponse)(*v.ResponseRange))
default:
fmt.Printf("\"Unknown\" : %q\n", fmt.Sprintf("%+v", v))
}
}
}
func (p *fieldsPrinter) Watch(resp v3.WatchResponse) {
p.hdr(&resp.Header)
for _, e := range resp.Events {
fmt.Println(`"Type" :`, e.Type)
if e.PrevKv != nil {
p.kv("Prev", e.PrevKv)
}
p.kv("", e.Kv)
}
}
func (p *fieldsPrinter) TimeToLive(r v3.LeaseTimeToLiveResponse, keys bool) {
p.hdr(r.ResponseHeader)
fmt.Println(`"ID" :`, r.ID)
fmt.Println(`"TTL" :`, r.TTL)
fmt.Println(`"GrantedTTL" :`, r.GrantedTTL)
for _, k := range r.Keys {
fmt.Printf("\"Key\" : %q\n", string(k))
}
}
func (p *fieldsPrinter) MemberList(r v3.MemberListResponse) {
p.hdr(r.Header)
for _, m := range r.Members {
fmt.Println(`"ID" :`, m.ID)
fmt.Printf("\"Name\" : %q\n", m.Name)
for _, u := range m.PeerURLs {
fmt.Printf("\"PeerURL\" : %q\n", u)
}
for _, u := range m.ClientURLs {
fmt.Printf("\"ClientURL\" : %q\n", u)
}
fmt.Println()
}
}
func (p *fieldsPrinter) EndpointStatus(eps []epStatus) {
for _, ep := range eps {
p.hdr(ep.Resp.Header)
fmt.Printf("\"Version\" : %q\n", ep.Resp.Version)
fmt.Println(`"DBSize" :"`, ep.Resp.DbSize)
fmt.Println(`"Leader" :"`, ep.Resp.Leader)
fmt.Println(`"RaftIndex" :"`, ep.Resp.RaftIndex)
fmt.Println(`"RaftTerm" :"`, ep.Resp.RaftTerm)
fmt.Printf("\"Endpoint\" : %q\n", ep.Ep)
fmt.Println()
}
}
func (p *fieldsPrinter) Alarm(r v3.AlarmResponse) {
p.hdr(r.Header)
for _, a := range r.Alarms {
fmt.Println(`"MemberID" :`, a.MemberID)
fmt.Println(`"AlarmType" :`, a.Alarm)
fmt.Println()
}
}
func (p *fieldsPrinter) DBStatus(r dbstatus) {
fmt.Println(`"Hash" :`, r.Hash)
fmt.Println(`"Revision" :`, r.Revision)
fmt.Println(`"Keys" :`, r.TotalKey)
fmt.Println(`"Size" :`, r.TotalSize)
}
func (p *fieldsPrinter) RoleAdd(role string, r v3.AuthRoleAddResponse) { p.hdr(r.Header) }
func (p *fieldsPrinter) RoleGet(role string, r v3.AuthRoleGetResponse) { p.hdr(r.Header) }
func (p *fieldsPrinter) RoleDelete(role string, r v3.AuthRoleDeleteResponse) { p.hdr(r.Header) }
func (p *fieldsPrinter) RoleList(r v3.AuthRoleListResponse) { p.hdr(r.Header) }
func (p *fieldsPrinter) RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse) {
p.hdr(r.Header)
}
func (p *fieldsPrinter) RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse) {
p.hdr(r.Header)
}
func (p *fieldsPrinter) UserAdd(user string, r v3.AuthUserAddResponse) { p.hdr(r.Header) }
func (p *fieldsPrinter) UserChangePassword(r v3.AuthUserChangePasswordResponse) { p.hdr(r.Header) }
func (p *fieldsPrinter) UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse) {
p.hdr(r.Header)
}
func (p *fieldsPrinter) UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse) {
p.hdr(r.Header)
}
func (p *fieldsPrinter) UserDelete(user string, r v3.AuthUserDeleteResponse) { p.hdr(r.Header) }

View File

@ -0,0 +1,41 @@
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command
import (
"encoding/json"
"fmt"
"os"
)
type jsonPrinter struct{ printer }
func newJSONPrinter() printer {
return &jsonPrinter{
&printerRPC{newPrinterUnsupported("json"), printJSON},
}
}
func (p *jsonPrinter) EndpointStatus(r []epStatus) { printJSON(r) }
func (p *jsonPrinter) DBStatus(r dbstatus) { printJSON(r) }
func printJSON(v interface{}) {
b, err := json.Marshal(v)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return
}
fmt.Println(string(b))
}

View File

@ -0,0 +1,64 @@
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command
import (
"fmt"
"os"
v3 "github.com/coreos/etcd/clientv3"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
mvccpb "github.com/coreos/etcd/mvcc/mvccpb"
)
type pbPrinter struct{ printer }
type pbMarshal interface {
Marshal() ([]byte, error)
}
func newPBPrinter() printer {
return &pbPrinter{
&printerRPC{newPrinterUnsupported("protobuf"), printPB},
}
}
func (p *pbPrinter) Watch(r v3.WatchResponse) {
evs := make([]*mvccpb.Event, len(r.Events))
for i, ev := range r.Events {
evs[i] = (*mvccpb.Event)(ev)
}
wr := pb.WatchResponse{
Header: &r.Header,
Events: evs,
CompactRevision: r.CompactRevision,
Canceled: r.Canceled,
Created: r.Created,
}
printPB(&wr)
}
func printPB(v interface{}) {
m, ok := v.(pbMarshal)
if !ok {
ExitWithError(ExitBadFeature, fmt.Errorf("marshal unsupported for type %T (%v)", v, v))
}
b, err := m.Marshal()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return
}
fmt.Printf(string(b))
}

View File

@ -0,0 +1,227 @@
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command
import (
"fmt"
"strings"
v3 "github.com/coreos/etcd/clientv3"
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
)
type simplePrinter struct {
isHex bool
valueOnly bool
}
func (s *simplePrinter) Del(resp v3.DeleteResponse) {
fmt.Println(resp.Deleted)
for _, kv := range resp.PrevKvs {
printKV(s.isHex, s.valueOnly, kv)
}
}
func (s *simplePrinter) Get(resp v3.GetResponse) {
for _, kv := range resp.Kvs {
printKV(s.isHex, s.valueOnly, kv)
}
}
func (s *simplePrinter) Put(r v3.PutResponse) {
fmt.Println("OK")
if r.PrevKv != nil {
printKV(s.isHex, s.valueOnly, r.PrevKv)
}
}
func (s *simplePrinter) Txn(resp v3.TxnResponse) {
if resp.Succeeded {
fmt.Println("SUCCESS")
} else {
fmt.Println("FAILURE")
}
for _, r := range resp.Responses {
fmt.Println("")
switch v := r.Response.(type) {
case *pb.ResponseOp_ResponseDeleteRange:
s.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
case *pb.ResponseOp_ResponsePut:
s.Put((v3.PutResponse)(*v.ResponsePut))
case *pb.ResponseOp_ResponseRange:
s.Get(((v3.GetResponse)(*v.ResponseRange)))
default:
fmt.Printf("unexpected response %+v\n", r)
}
}
}
func (s *simplePrinter) Watch(resp v3.WatchResponse) {
for _, e := range resp.Events {
fmt.Println(e.Type)
if e.PrevKv != nil {
printKV(s.isHex, s.valueOnly, e.PrevKv)
}
printKV(s.isHex, s.valueOnly, e.Kv)
}
}
func (s *simplePrinter) TimeToLive(resp v3.LeaseTimeToLiveResponse, keys bool) {
txt := fmt.Sprintf("lease %016x granted with TTL(%ds), remaining(%ds)", resp.ID, resp.GrantedTTL, resp.TTL)
if keys {
ks := make([]string, len(resp.Keys))
for i := range resp.Keys {
ks[i] = string(resp.Keys[i])
}
txt += fmt.Sprintf(", attached keys(%v)", ks)
}
fmt.Println(txt)
}
func (s *simplePrinter) Alarm(resp v3.AlarmResponse) {
for _, e := range resp.Alarms {
fmt.Printf("%+v\n", e)
}
}
func (s *simplePrinter) MemberAdd(r v3.MemberAddResponse) {
fmt.Printf("Member %16x added to cluster %16x\n", r.Member.ID, r.Header.ClusterId)
}
func (s *simplePrinter) MemberRemove(id uint64, r v3.MemberRemoveResponse) {
fmt.Printf("Member %16x removed from cluster %16x\n", id, r.Header.ClusterId)
}
func (s *simplePrinter) MemberUpdate(id uint64, r v3.MemberUpdateResponse) {
fmt.Printf("Member %16x updated in cluster %16x\n", id, r.Header.ClusterId)
}
func (s *simplePrinter) MemberList(resp v3.MemberListResponse) {
_, rows := makeMemberListTable(resp)
for _, row := range rows {
fmt.Println(strings.Join(row, ", "))
}
}
func (s *simplePrinter) EndpointStatus(statusList []epStatus) {
_, rows := makeEndpointStatusTable(statusList)
for _, row := range rows {
fmt.Println(strings.Join(row, ", "))
}
}
func (s *simplePrinter) DBStatus(ds dbstatus) {
_, rows := makeDBStatusTable(ds)
for _, row := range rows {
fmt.Println(strings.Join(row, ", "))
}
}
func (s *simplePrinter) RoleAdd(role string, r v3.AuthRoleAddResponse) {
fmt.Printf("Role %s created\n", role)
}
func (s *simplePrinter) RoleGet(role string, r v3.AuthRoleGetResponse) {
fmt.Printf("Role %s\n", role)
fmt.Println("KV Read:")
printRange := func(perm *v3.Permission) {
sKey := string(perm.Key)
sRangeEnd := string(perm.RangeEnd)
fmt.Printf("\t[%s, %s)", sKey, sRangeEnd)
if strings.Compare(v3.GetPrefixRangeEnd(sKey), sRangeEnd) == 0 {
fmt.Printf(" (prefix %s)", sKey)
}
fmt.Printf("\n")
}
for _, perm := range r.Perm {
if perm.PermType == v3.PermRead || perm.PermType == v3.PermReadWrite {
if len(perm.RangeEnd) == 0 {
fmt.Printf("\t%s\n", string(perm.Key))
} else {
printRange((*v3.Permission)(perm))
}
}
}
fmt.Println("KV Write:")
for _, perm := range r.Perm {
if perm.PermType == v3.PermWrite || perm.PermType == v3.PermReadWrite {
if len(perm.RangeEnd) == 0 {
fmt.Printf("\t%s\n", string(perm.Key))
} else {
printRange((*v3.Permission)(perm))
}
}
}
}
func (s *simplePrinter) RoleList(r v3.AuthRoleListResponse) {
for _, role := range r.Roles {
fmt.Printf("%s\n", role)
}
}
func (s *simplePrinter) RoleDelete(role string, r v3.AuthRoleDeleteResponse) {
fmt.Printf("Role %s deleted\n", role)
}
func (s *simplePrinter) RoleGrantPermission(role string, r v3.AuthRoleGrantPermissionResponse) {
fmt.Printf("Role %s updated\n", role)
}
func (s *simplePrinter) RoleRevokePermission(role string, key string, end string, r v3.AuthRoleRevokePermissionResponse) {
if len(end) == 0 {
fmt.Printf("Permission of key %s is revoked from role %s\n", key, role)
return
}
fmt.Printf("Permission of range [%s, %s) is revoked from role %s\n", key, end, role)
}
func (s *simplePrinter) UserAdd(name string, r v3.AuthUserAddResponse) {
fmt.Printf("User %s created\n", name)
}
func (s *simplePrinter) UserGet(name string, r v3.AuthUserGetResponse) {
fmt.Printf("User: %s\n", name)
fmt.Printf("Roles:")
for _, role := range r.Roles {
fmt.Printf(" %s", role)
}
fmt.Printf("\n")
}
func (s *simplePrinter) UserChangePassword(v3.AuthUserChangePasswordResponse) {
fmt.Println("Password updated")
}
func (s *simplePrinter) UserGrantRole(user string, role string, r v3.AuthUserGrantRoleResponse) {
fmt.Printf("Role %s is granted to user %s\n", role, user)
}
func (s *simplePrinter) UserRevokeRole(user string, role string, r v3.AuthUserRevokeRoleResponse) {
fmt.Printf("Role %s is revoked from user %s\n", role, user)
}
func (s *simplePrinter) UserDelete(user string, r v3.AuthUserDeleteResponse) {
fmt.Printf("User %s deleted\n", user)
}
func (s *simplePrinter) UserList(r v3.AuthUserListResponse) {
for _, user := range r.Users {
fmt.Printf("%s\n", user)
}
}

View File

@ -0,0 +1,53 @@
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command
import (
"os"
"github.com/olekukonko/tablewriter"
v3 "github.com/coreos/etcd/clientv3"
)
type tablePrinter struct{ printer }
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 (tp *tablePrinter) EndpointStatus(r []epStatus) {
hdr, rows := makeEndpointStatusTable(r)
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(hdr)
for _, row := range rows {
table.Append(row)
}
table.Render()
}
func (tp *tablePrinter) DBStatus(r dbstatus) {
hdr, rows := makeDBStatusTable(r)
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader(hdr)
for _, row := range rows {
table.Append(row)
}
table.Render()
}

View File

@ -16,7 +16,6 @@ package command
import (
"fmt"
"strings"
"github.com/coreos/etcd/clientv3"
"github.com/spf13/cobra"
@ -102,12 +101,12 @@ func roleAddCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitBadArgs, fmt.Errorf("role add command requires role name as its argument."))
}
_, err := mustClientFromCmd(cmd).Auth.RoleAdd(context.TODO(), args[0])
resp, err := mustClientFromCmd(cmd).Auth.RoleAdd(context.TODO(), args[0])
if err != nil {
ExitWithError(ExitError, err)
}
fmt.Printf("Role %s created\n", args[0])
display.RoleAdd(args[0], *resp)
}
// roleDeleteCommandFunc executes the "role delete" command.
@ -116,47 +115,12 @@ func roleDeleteCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitBadArgs, fmt.Errorf("role delete command requires role name as its argument."))
}
_, err := mustClientFromCmd(cmd).Auth.RoleDelete(context.TODO(), args[0])
resp, err := mustClientFromCmd(cmd).Auth.RoleDelete(context.TODO(), args[0])
if err != nil {
ExitWithError(ExitError, err)
}
fmt.Printf("Role %s deleted\n", args[0])
}
func printRolePermissions(name string, resp *clientv3.AuthRoleGetResponse) {
fmt.Printf("Role %s\n", name)
fmt.Println("KV Read:")
printRange := func(perm *clientv3.Permission) {
sKey := string(perm.Key)
sRangeEnd := string(perm.RangeEnd)
fmt.Printf("\t[%s, %s)", sKey, sRangeEnd)
if strings.Compare(clientv3.GetPrefixRangeEnd(sKey), sRangeEnd) == 0 {
fmt.Printf(" (prefix %s)", sKey)
}
fmt.Printf("\n")
}
for _, perm := range resp.Perm {
if perm.PermType == clientv3.PermRead || perm.PermType == clientv3.PermReadWrite {
if len(perm.RangeEnd) == 0 {
fmt.Printf("\t%s\n", string(perm.Key))
} else {
printRange((*clientv3.Permission)(perm))
}
}
}
fmt.Println("KV Write:")
for _, perm := range resp.Perm {
if perm.PermType == clientv3.PermWrite || perm.PermType == clientv3.PermReadWrite {
if len(perm.RangeEnd) == 0 {
fmt.Printf("\t%s\n", string(perm.Key))
} else {
printRange((*clientv3.Permission)(perm))
}
}
}
display.RoleDelete(args[0], *resp)
}
// roleGetCommandFunc executes the "role get" command.
@ -171,7 +135,7 @@ func roleGetCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitError, err)
}
printRolePermissions(name, resp)
display.RoleGet(name, *resp)
}
// roleListCommandFunc executes the "role list" command.
@ -185,9 +149,7 @@ func roleListCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitError, err)
}
for _, role := range resp.Roles {
fmt.Printf("%s\n", role)
}
display.RoleList(*resp)
}
// roleGrantPermissionCommandFunc executes the "role grant-permission" command.
@ -211,12 +173,12 @@ func roleGrantPermissionCommandFunc(cmd *cobra.Command, args []string) {
rangeEnd = clientv3.GetPrefixRangeEnd(args[2])
}
_, err = mustClientFromCmd(cmd).Auth.RoleGrantPermission(context.TODO(), args[0], args[2], rangeEnd, perm)
resp, err := mustClientFromCmd(cmd).Auth.RoleGrantPermission(context.TODO(), args[0], args[2], rangeEnd, perm)
if err != nil {
ExitWithError(ExitError, err)
}
fmt.Printf("Role %s updated\n", args[0])
display.RoleGrantPermission(args[0], *resp)
}
// roleRevokePermissionCommandFunc executes the "role revoke-permission" command.
@ -230,14 +192,9 @@ func roleRevokePermissionCommandFunc(cmd *cobra.Command, args []string) {
rangeEnd = args[2]
}
_, err := mustClientFromCmd(cmd).Auth.RoleRevokePermission(context.TODO(), args[0], args[1], rangeEnd)
resp, err := mustClientFromCmd(cmd).Auth.RoleRevokePermission(context.TODO(), args[0], args[1], rangeEnd)
if err != nil {
ExitWithError(ExitError, err)
}
if len(rangeEnd) == 0 {
fmt.Printf("Permission of key %s is revoked from role %s\n", args[1], args[0])
} else {
fmt.Printf("Permission of range [%s, %s) is revoked from role %s\n", args[1], rangeEnd, args[0])
}
display.RoleRevokePermission(args[0], args[1], rangeEnd, *resp)
}

View File

@ -142,12 +142,12 @@ func userAddCommandFunc(cmd *cobra.Command, args []string) {
}
}
_, err := mustClientFromCmd(cmd).Auth.UserAdd(context.TODO(), user, password)
resp, err := mustClientFromCmd(cmd).Auth.UserAdd(context.TODO(), user, password)
if err != nil {
ExitWithError(ExitError, err)
}
fmt.Printf("User %s created\n", user)
display.UserAdd(user, *resp)
}
// userDeleteCommandFunc executes the "user delete" command.
@ -156,12 +156,11 @@ func userDeleteCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitBadArgs, fmt.Errorf("user delete command requires user name as its argument."))
}
_, err := mustClientFromCmd(cmd).Auth.UserDelete(context.TODO(), args[0])
resp, err := mustClientFromCmd(cmd).Auth.UserDelete(context.TODO(), args[0])
if err != nil {
ExitWithError(ExitError, err)
}
fmt.Printf("User %s deleted\n", args[0])
display.UserDelete(args[0], *resp)
}
// userGetCommandFunc executes the "user get" command.
@ -177,23 +176,18 @@ func userGetCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitError, err)
}
fmt.Printf("User: %s\n", name)
if !userShowDetail {
fmt.Printf("Roles:")
for _, role := range resp.Roles {
fmt.Printf(" %s", role)
}
fmt.Printf("\n")
} else {
if userShowDetail {
fmt.Printf("User: %s\n", name)
for _, role := range resp.Roles {
fmt.Printf("\n")
roleResp, err := client.Auth.RoleGet(context.TODO(), role)
if err != nil {
ExitWithError(ExitError, err)
}
printRolePermissions(role, roleResp)
display.RoleGet(role, *roleResp)
}
} else {
display.UserGet(name, *resp)
}
}
@ -208,9 +202,7 @@ func userListCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitError, err)
}
for _, user := range resp.Users {
fmt.Printf("%s\n", user)
}
display.UserList(*resp)
}
// userChangePasswordCommandFunc executes the "user passwd" command.
@ -227,12 +219,12 @@ func userChangePasswordCommandFunc(cmd *cobra.Command, args []string) {
password = readPasswordInteractive(args[0])
}
_, err := mustClientFromCmd(cmd).Auth.UserChangePassword(context.TODO(), args[0], password)
resp, err := mustClientFromCmd(cmd).Auth.UserChangePassword(context.TODO(), args[0], password)
if err != nil {
ExitWithError(ExitError, err)
}
fmt.Println("Password updated")
display.UserChangePassword(*resp)
}
// userGrantRoleCommandFunc executes the "user grant-role" command.
@ -241,12 +233,12 @@ func userGrantRoleCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitBadArgs, fmt.Errorf("user grant command requires user name and role name as its argument."))
}
_, err := mustClientFromCmd(cmd).Auth.UserGrantRole(context.TODO(), args[0], args[1])
resp, err := mustClientFromCmd(cmd).Auth.UserGrantRole(context.TODO(), args[0], args[1])
if err != nil {
ExitWithError(ExitError, err)
}
fmt.Printf("Role %s is granted to user %s\n", args[1], args[0])
display.UserGrantRole(args[0], args[1], *resp)
}
// userRevokeRoleCommandFunc executes the "user revoke-role" command.
@ -255,12 +247,12 @@ func userRevokeRoleCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitBadArgs, fmt.Errorf("user revoke-role requires user name and role name as its argument."))
}
_, err := mustClientFromCmd(cmd).Auth.UserRevokeRole(context.TODO(), args[0], args[1])
resp, err := mustClientFromCmd(cmd).Auth.UserRevokeRole(context.TODO(), args[0], args[1])
if err != nil {
ExitWithError(ExitError, err)
}
fmt.Printf("Role %s is revoked from user %s\n", args[1], args[0])
display.UserRevokeRole(args[0], args[1], *resp)
}
func readPasswordInteractive(name string) string {