mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #4597 from heyitsanthony/etcdctlv3-format
etcdctlv3: add formatting for json and protobuf
This commit is contained in:
commit
86e04d5ff3
@ -13,13 +13,19 @@ PUT assigns the specified value with the specified key. If key already holds a v
|
||||
|
||||
#### Return value
|
||||
|
||||
Simple reply
|
||||
##### Simple reply
|
||||
|
||||
- OK if PUT executed correctly. Exit code is zero.
|
||||
|
||||
- Error string if PUT failed. Exit code is non-zero.
|
||||
|
||||
TODO: probably json and binary encoded proto
|
||||
##### JSON reply
|
||||
|
||||
The JSON encoding of the PUT [RPC response][etcdrpc].
|
||||
|
||||
##### Protobuf reply
|
||||
|
||||
The protobuf encoding of the PUT [RPC response][etcdrpc].
|
||||
|
||||
#### Examples
|
||||
|
||||
@ -60,13 +66,19 @@ TODO: add consistency, from, prefix
|
||||
|
||||
#### Return value
|
||||
|
||||
Simple reply
|
||||
##### Simple reply
|
||||
|
||||
- \<key\>\n\<value\>\n\<next_key\>\n\<next_value\>...
|
||||
|
||||
- Error string if GET failed. Exit code is non-zero.
|
||||
|
||||
TODO: probably json and binary encoded proto
|
||||
##### JSON reply
|
||||
|
||||
The JSON encoding of the [RPC message][etcdrpc] for a key-value pair for each fetched key-value.
|
||||
|
||||
##### Protobuf reply
|
||||
|
||||
The protobuf encoding of the [RPC message][etcdrpc] for a key-value pair for each fetched key-value.
|
||||
|
||||
#### Examples
|
||||
|
||||
@ -78,7 +90,7 @@ bar
|
||||
|
||||
#### Notes
|
||||
|
||||
If any key or value contains non-printable characters or control characters, the output in text format (e.g. simple reply or JSON reply) might be ambiguous.
|
||||
If any key or value contains non-printable characters or control characters, the output in text format (e.g. simple reply) might be ambiguous.
|
||||
Adding `--hex` to print key or value as hex encode string in text format can resolve this issue.
|
||||
|
||||
### DEL [options] \<key\> [range_end]
|
||||
@ -91,13 +103,19 @@ TODO: --prefix, --from
|
||||
|
||||
#### Return value
|
||||
|
||||
Simple reply
|
||||
##### Simple reply
|
||||
|
||||
- The number of keys that were removed in decimal if DEL executed correctly. Exit code is zero.
|
||||
|
||||
- Error string if DEL failed. Exit code is non-zero.
|
||||
|
||||
TODO: probably json and binary encoded proto
|
||||
##### JSON reply
|
||||
|
||||
The JSON encoding of the DeleteRange [RPC response][etcdrpc].
|
||||
|
||||
##### Protobuf reply
|
||||
|
||||
The protobuf encoding of the DeleteRange [RPC response][etcdrpc].
|
||||
|
||||
#### Examples
|
||||
|
||||
@ -142,7 +160,7 @@ TODO: non-interactive mode
|
||||
|
||||
#### Return value
|
||||
|
||||
Simple reply
|
||||
##### Simple reply
|
||||
|
||||
- SUCCESS if etcd processed the transaction success list, FAILURE if etcd processed the transaction failure list.
|
||||
|
||||
@ -150,7 +168,13 @@ Simple reply
|
||||
|
||||
- Additional error string if TXN failed. Exit code is non-zero.
|
||||
|
||||
TODO: probably json and binary encoded proto
|
||||
##### JSON reply
|
||||
|
||||
The JSON encoding of the Txn [RPC response][etcdrpc].
|
||||
|
||||
##### Protobuf reply
|
||||
|
||||
The protobuf encoding of the Txn [RPC response][etcdrpc].
|
||||
|
||||
#### Examples
|
||||
|
||||
@ -205,7 +229,13 @@ watch [options] <key or prefix>\n
|
||||
|
||||
- Additional error string if WATCH failed. Exit code is non-zero.
|
||||
|
||||
TODO: probably json and binary encoded proto
|
||||
##### JSON reply
|
||||
|
||||
The JSON encoding of the [RPC message][storagerpc] for each received Event.
|
||||
|
||||
##### Protobuf reply
|
||||
|
||||
The protobuf encoding of the [RPC message][storagerpc] for each received Event.
|
||||
|
||||
#### Examples
|
||||
|
||||
@ -265,3 +295,12 @@ Simple reply
|
||||
```
|
||||
|
||||
[mirror]: ./doc/mirror_maker.md
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- JSON encoding for keys and values uses base64 since they are byte strings.
|
||||
|
||||
|
||||
[etcdrpc]: ../etcdserver/etcdserverpb/rpc.proto
|
||||
[storagerpc]: ../storage/storagepb/kv.proto
|
||||
|
@ -40,7 +40,7 @@ func delCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
printDeleteResponse(*resp)
|
||||
display.Del(*resp)
|
||||
}
|
||||
|
||||
func getDelOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) {
|
||||
@ -54,9 +54,3 @@ func getDelOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) {
|
||||
}
|
||||
return key, opts
|
||||
}
|
||||
|
||||
func printDeleteResponse(resp clientv3.DeleteResponse) {
|
||||
// TODO: add number of key removed into the response of delete.
|
||||
// TODO: print out the number of removed keys.
|
||||
fmt.Println(0)
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ var (
|
||||
getLimit int64
|
||||
getSortOrder string
|
||||
getSortTarget string
|
||||
getHex bool
|
||||
)
|
||||
|
||||
// NewGetCommand returns the cobra command for "get".
|
||||
@ -41,7 +40,6 @@ func NewGetCommand() *cobra.Command {
|
||||
cmd.Flags().StringVar(&getSortOrder, "order", "", "order of results; ASCEND or DESCEND")
|
||||
cmd.Flags().StringVar(&getSortTarget, "sort-by", "", "sort target; CREATE, KEY, MODIFY, VALUE, or VERSION")
|
||||
cmd.Flags().Int64Var(&getLimit, "limit", 0, "maximum number of results")
|
||||
cmd.Flags().BoolVar(&getHex, "hex", false, "print out key and value as hex encode string for text format")
|
||||
// TODO: add fromkey.
|
||||
// TODO: add prefix.
|
||||
// TODO: add consistency.
|
||||
@ -57,7 +55,8 @@ func getCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
printGetResponse(*resp, getHex)
|
||||
|
||||
display.Get(*resp)
|
||||
}
|
||||
|
||||
func getGetOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) {
|
||||
@ -107,9 +106,3 @@ func getGetOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) {
|
||||
opts = append(opts, clientv3.WithSort(sortByTarget, sortByOrder))
|
||||
return key, opts
|
||||
}
|
||||
|
||||
func printGetResponse(resp clientv3.GetResponse, isHex bool) {
|
||||
for _, kv := range resp.Kvs {
|
||||
printKV(isHex, kv)
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,13 @@ import (
|
||||
type GlobalFlags struct {
|
||||
Endpoints string
|
||||
TLS transport.TLSInfo
|
||||
|
||||
OutputFormat string
|
||||
IsHex bool
|
||||
}
|
||||
|
||||
var display printer = &simplePrinter{}
|
||||
|
||||
func mustClientFromCmd(cmd *cobra.Command) *clientv3.Client {
|
||||
endpoint, err := cmd.Flags().GetString("endpoint")
|
||||
if err != nil {
|
||||
@ -57,6 +62,12 @@ func mustClientFromCmd(cmd *cobra.Command) *clientv3.Client {
|
||||
ExitWithError(ExitBadArgs, errors.New("empty string is passed to --cacert option"))
|
||||
}
|
||||
|
||||
isHex, _ := cmd.Flags().GetBool("hex")
|
||||
outputType, _ := cmd.Flags().GetString("write-out")
|
||||
if display = NewPrinter(outputType, isHex); display == nil {
|
||||
ExitWithError(ExitBadFeature, errors.New("unsupported output format"))
|
||||
}
|
||||
|
||||
return mustClient(endpoint, cert, key, cacert)
|
||||
}
|
||||
|
||||
@ -90,6 +101,7 @@ func mustClient(endpoint, cert, key, cacert string) *clientv3.Client {
|
||||
if err != nil {
|
||||
ExitWithError(ExitBadConnection, err)
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
|
146
etcdctlv3/command/printer.go
Normal file
146
etcdctlv3/command/printer.go
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright 2016 CoreOS, Inc.
|
||||
//
|
||||
// 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"
|
||||
|
||||
v3 "github.com/coreos/etcd/clientv3"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
spb "github.com/coreos/etcd/storage/storagepb"
|
||||
)
|
||||
|
||||
type printer interface {
|
||||
Del(v3.DeleteResponse)
|
||||
Get(v3.GetResponse)
|
||||
Put(v3.PutResponse)
|
||||
Txn(v3.TxnResponse)
|
||||
Watch(v3.WatchResponse)
|
||||
}
|
||||
|
||||
func NewPrinter(printerType string, isHex bool) printer {
|
||||
switch printerType {
|
||||
case "simple":
|
||||
return &simplePrinter{isHex: isHex}
|
||||
case "json":
|
||||
return &jsonPrinter{}
|
||||
case "protobuf":
|
||||
return &pbPrinter{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type simplePrinter struct {
|
||||
isHex bool
|
||||
}
|
||||
|
||||
func (s *simplePrinter) Del(v3.DeleteResponse) {
|
||||
// TODO: add number of key removed into the response of delete.
|
||||
// TODO: print out the number of removed keys.
|
||||
fmt.Println(0)
|
||||
}
|
||||
|
||||
func (s *simplePrinter) Get(resp v3.GetResponse) {
|
||||
for _, kv := range resp.Kvs {
|
||||
printKV(s.isHex, kv)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *simplePrinter) Put(r v3.PutResponse) { fmt.Println("OK") }
|
||||
|
||||
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.ResponseUnion_ResponseDeleteRange:
|
||||
s.Del((v3.DeleteResponse)(*v.ResponseDeleteRange))
|
||||
case *pb.ResponseUnion_ResponsePut:
|
||||
s.Put((v3.PutResponse)(*v.ResponsePut))
|
||||
case *pb.ResponseUnion_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)
|
||||
printKV(s.isHex, e.Kv)
|
||||
}
|
||||
}
|
||||
|
||||
type jsonPrinter struct{}
|
||||
|
||||
func (p *jsonPrinter) Del(r v3.DeleteResponse) { printJSON(r) }
|
||||
func (p *jsonPrinter) Get(r v3.GetResponse) {
|
||||
for _, kv := range r.Kvs {
|
||||
printJSON(kv)
|
||||
}
|
||||
}
|
||||
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 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 printPB(m pbMarshal) {
|
||||
b, err := m.Marshal()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf(string(b))
|
||||
}
|
@ -64,7 +64,7 @@ func putCommandFunc(cmd *cobra.Command, args []string) {
|
||||
if err != nil {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
printPutResponse(*resp)
|
||||
display.Put(*resp)
|
||||
}
|
||||
|
||||
func getPutOp(cmd *cobra.Command, args []string) (string, string, []clientv3.OpOption) {
|
||||
@ -90,7 +90,3 @@ func getPutOp(cmd *cobra.Command, args []string) (string, string, []clientv3.OpO
|
||||
|
||||
return key, value, opts
|
||||
}
|
||||
|
||||
func printPutResponse(resp clientv3.PutResponse) {
|
||||
fmt.Println("OK")
|
||||
}
|
||||
|
@ -24,12 +24,10 @@ import (
|
||||
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/spf13/cobra"
|
||||
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||
)
|
||||
|
||||
var (
|
||||
txnInteractive bool
|
||||
txnHex bool
|
||||
)
|
||||
|
||||
// NewTxnCommand returns the cobra command for "txn".
|
||||
@ -40,7 +38,6 @@ func NewTxnCommand() *cobra.Command {
|
||||
Run: txnCommandFunc,
|
||||
}
|
||||
cmd.Flags().BoolVarP(&txnInteractive, "interactive", "i", false, "input transaction in interactive mode")
|
||||
cmd.Flags().BoolVar(&txnHex, "hex", false, "print out key-values as hex encoded strings")
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -69,7 +66,7 @@ func txnCommandFunc(cmd *cobra.Command, args []string) {
|
||||
ExitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
printTxnResponse(*resp, txnHex)
|
||||
display.Txn(*resp)
|
||||
}
|
||||
|
||||
func readCompares(r *bufio.Reader) (cmps []clientv3.Cmp) {
|
||||
@ -202,25 +199,3 @@ func parseCompare(line string) (*clientv3.Cmp, error) {
|
||||
|
||||
return &cmp, nil
|
||||
}
|
||||
|
||||
func printTxnResponse(resp clientv3.TxnResponse, isHex bool) {
|
||||
if resp.Succeeded {
|
||||
fmt.Println("SUCCESS")
|
||||
} else {
|
||||
fmt.Println("FAILURE")
|
||||
}
|
||||
|
||||
for _, r := range resp.Responses {
|
||||
fmt.Println("")
|
||||
switch v := r.Response.(type) {
|
||||
case *pb.ResponseUnion_ResponseDeleteRange:
|
||||
printDeleteResponse((clientv3.DeleteResponse)(*v.ResponseDeleteRange))
|
||||
case *pb.ResponseUnion_ResponsePut:
|
||||
printPutResponse((clientv3.PutResponse)(*v.ResponsePut))
|
||||
case *pb.ResponseUnion_ResponseRange:
|
||||
printGetResponse(((clientv3.GetResponse)(*v.ResponseRange)), isHex)
|
||||
default:
|
||||
fmt.Printf("unexpected response %+v\n", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ import (
|
||||
var (
|
||||
watchRev int64
|
||||
watchPrefix bool
|
||||
watchHex bool
|
||||
watchInteractive bool
|
||||
)
|
||||
|
||||
@ -40,7 +39,6 @@ func NewWatchCommand() *cobra.Command {
|
||||
Run: watchCommandFunc,
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&watchHex, "hex", false, "print out key and value as hex encode string for text format")
|
||||
cmd.Flags().BoolVarP(&watchInteractive, "interactive", "i", false, "interactive mode")
|
||||
cmd.Flags().BoolVar(&watchPrefix, "prefix", false, "watch on a prefix if prefix is set")
|
||||
cmd.Flags().Int64Var(&watchRev, "rev", 0, "revision to start watching")
|
||||
@ -68,7 +66,7 @@ func watchCommandFunc(cmd *cobra.Command, args []string) {
|
||||
} else {
|
||||
wc = w.Watch(context.TODO(), args[0], watchRev)
|
||||
}
|
||||
printWatchCh(wc, watchHex)
|
||||
printWatchCh(wc)
|
||||
err := w.Close()
|
||||
if err == nil {
|
||||
ExitWithError(ExitInterrupted, fmt.Errorf("watch is canceled by the server"))
|
||||
@ -122,19 +120,12 @@ func watchInteractiveFunc(cmd *cobra.Command, args []string) {
|
||||
} else {
|
||||
ch = w.Watch(context.TODO(), key, watchRev)
|
||||
}
|
||||
go printWatchCh(ch, watchHex)
|
||||
go printWatchCh(ch)
|
||||
}
|
||||
}
|
||||
|
||||
func printWatchCh(ch clientv3.WatchChan, hex bool) {
|
||||
func printWatchCh(ch clientv3.WatchChan) {
|
||||
for resp := range ch {
|
||||
printWatchResponse(resp, hex)
|
||||
}
|
||||
}
|
||||
|
||||
func printWatchResponse(resp clientv3.WatchResponse, hex bool) {
|
||||
for _, e := range resp.Events {
|
||||
fmt.Println(e.Type)
|
||||
printKV(hex, e.Kv)
|
||||
display.Watch(resp)
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,9 @@ var (
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVar(&globalFlags.Endpoints, "endpoint", "127.0.0.1:2378", "gRPC endpoint")
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&globalFlags.OutputFormat, "write-out", "w", "simple", "set the output format (simple, json, protobuf)")
|
||||
rootCmd.PersistentFlags().BoolVar(&globalFlags.IsHex, "hex", false, "print byte strings as hex encoded strings")
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.CertFile, "cert", "", "identify secure client using this TLS certificate file")
|
||||
rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.KeyFile, "key", "", "identify secure client using this TLS key file")
|
||||
rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.CAFile, "cacert", "", "verify certificates of TLS-enabled secure servers using this CA bundle")
|
||||
|
Loading…
x
Reference in New Issue
Block a user