Browse Source

Merge pull request #4562 from xiang90/get

etcdctlv3: better get command
Xiang Li 9 years ago
parent
commit
a22bb7a4e8
4 changed files with 165 additions and 104 deletions
  1. 39 0
      etcdctlv3/README.md
  2. 125 0
      etcdctlv3/command/get_command.go
  3. 0 103
      etcdctlv3/command/range_command.go
  4. 1 1
      etcdctlv3/main.go

+ 39 - 0
etcdctlv3/README.md

@@ -42,6 +42,45 @@ Insert '--' for workaround:
 ./etcdctl put -- <key> <value>
 ```
 
+### GET [options] \<key\> [range_end]
+
+GET gets the key or a range of keys [key, range_end) if `range-end` is given.
+
+#### Options
+
+- hex -- print out key and value as hex encode string
+
+- limit -- maximum number of results
+
+- order -- order of results; ASCEND or DESCEND
+
+- sort-by -- sort target; CREATE, KEY, MODIFY, VALUE, or VERSION
+
+TODO: add consistency, from, prefix
+
+#### Return value
+
+Simple reply
+
+- \<key\>\r\n\<value\>\r\n\<next_key\>\r\n\<next_value\>...
+
+- Error string if GET failed. Exit code is non-zero.
+
+TODO: probably json and binary encoded proto
+
+#### Examples
+
+``` bash
+./etcdctl get foo
+foo
+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.
+Adding `--hex` to print key or value as hex encode string in text format can resolve this issue.
+
 ### DEL [options] \<key\> [range_end]
 
 Removes the specified key or range of keys [key, range_end) if `range-end` is given.

+ 125 - 0
etcdctlv3/command/get_command.go

@@ -0,0 +1,125 @@
+// Copyright 2015 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/hex"
+	"fmt"
+	"strings"
+
+	"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"
+)
+
+var (
+	getLimit      int64
+	getSortOrder  string
+	getSortTarget string
+	getHex        bool
+)
+
+// NewGetCommand returns the cobra command for "get".
+func NewGetCommand() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "get [options] <key> [range_end]",
+		Short: "Get gets the key or a range of keys.",
+		Run:   getCommandFunc,
+	}
+
+	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.
+	return cmd
+}
+
+// getCommandFunc executes the "get" command.
+func getCommandFunc(cmd *cobra.Command, args []string) {
+	if len(args) == 0 {
+		ExitWithError(ExitBadArgs, fmt.Errorf("range command needs arguments."))
+	}
+
+	opts := []clientv3.OpOption{}
+	key := args[0]
+	if len(args) > 1 {
+		opts = append(opts, clientv3.WithRange(args[1]))
+	}
+	opts = append(opts, clientv3.WithLimit(getLimit))
+
+	sortByOrder := clientv3.SortNone
+	sortOrder := strings.ToUpper(getSortOrder)
+	switch {
+	case sortOrder == "ASCEND":
+		sortByOrder = clientv3.SortAscend
+	case sortOrder == "DESCEND":
+		sortByOrder = clientv3.SortDescend
+	case sortOrder == "":
+		// nothing
+	default:
+		ExitWithError(ExitBadFeature, fmt.Errorf("bad sort order %v", getSortOrder))
+	}
+
+	sortByTarget := clientv3.SortByKey
+	sortTarget := strings.ToUpper(getSortTarget)
+	switch {
+	case sortTarget == "CREATE":
+		sortByTarget = clientv3.SortByCreatedRev
+	case sortTarget == "KEY":
+		sortByTarget = clientv3.SortByKey
+	case sortTarget == "MODIFY":
+		sortByTarget = clientv3.SortByModifiedRev
+	case sortTarget == "VALUE":
+		sortByTarget = clientv3.SortByValue
+	case sortTarget == "VERSION":
+		sortByTarget = clientv3.SortByVersion
+	case sortTarget == "":
+		// nothing
+	default:
+		ExitWithError(ExitBadFeature, fmt.Errorf("bad sort target %v", getSortTarget))
+	}
+
+	opts = append(opts, clientv3.WithSort(sortByTarget, sortByOrder))
+
+	c := mustClientFromCmd(cmd)
+	kvapi := clientv3.NewKV(c)
+	resp, err := kvapi.Get(context.TODO(), key, opts...)
+	if err != nil {
+		ExitWithError(ExitError, err)
+	}
+
+	for _, kv := range resp.Kvs {
+		k, v := string(kv.Key), string(kv.Value)
+		if getHex {
+			k = addHexPrefix(hex.EncodeToString(kv.Key))
+			v = addHexPrefix(hex.EncodeToString(kv.Value))
+		}
+		fmt.Printf("%s\r\n%s\r\n", k, v)
+	}
+}
+
+func addHexPrefix(s string) string {
+	ns := make([]byte, len(s)*2)
+	for i := 0; i < len(s); i += 2 {
+		ns[i*2] = '\\'
+		ns[i*2+1] = 'x'
+		ns[i*2+2] = s[i]
+		ns[i*2+3] = s[i+1]
+	}
+	return string(ns)
+}

+ 0 - 103
etcdctlv3/command/range_command.go

@@ -1,103 +0,0 @@
-// Copyright 2015 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 (
-	"fmt"
-	"strings"
-
-	"github.com/coreos/etcd/Godeps/_workspace/src/github.com/spf13/cobra"
-	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
-	pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
-)
-
-var (
-	rangeLimit      int
-	rangeSortOrder  string
-	rangeSortTarget string
-)
-
-// NewRangeCommand returns the cobra command for "range".
-func NewRangeCommand() *cobra.Command {
-	cmd := &cobra.Command{
-		Use:   "range",
-		Short: "Range gets the keys in the range from the store.",
-		Run:   rangeCommandFunc,
-	}
-	cmd.Flags().StringVar(&rangeSortOrder, "order", "", "order of results; ASCEND or DESCEND")
-	cmd.Flags().StringVar(&rangeSortTarget, "sort-by", "", "sort target; CREATE, KEY, MODIFY, VALUE, or VERSION")
-	cmd.Flags().IntVar(&rangeLimit, "limit", 0, "maximum number of results")
-	return cmd
-}
-
-// rangeCommandFunc executes the "range" command.
-func rangeCommandFunc(cmd *cobra.Command, args []string) {
-	if len(args) == 0 {
-		ExitWithError(ExitBadArgs, fmt.Errorf("range command needs arguments."))
-	}
-
-	var rangeEnd []byte
-	key := []byte(args[0])
-	if len(args) > 1 {
-		rangeEnd = []byte(args[1])
-	}
-
-	sortByOrder := pb.RangeRequest_NONE
-	sortOrder := strings.ToUpper(rangeSortOrder)
-	switch {
-	case sortOrder == "ASCEND":
-		sortByOrder = pb.RangeRequest_ASCEND
-	case sortOrder == "DESCEND":
-		sortByOrder = pb.RangeRequest_DESCEND
-	case sortOrder == "":
-		sortByOrder = pb.RangeRequest_NONE
-	default:
-		ExitWithError(ExitBadFeature, fmt.Errorf("bad sort order %v", rangeSortOrder))
-	}
-
-	sortByTarget := pb.RangeRequest_KEY
-	sortTarget := strings.ToUpper(rangeSortTarget)
-	switch {
-	case sortTarget == "CREATE":
-		sortByTarget = pb.RangeRequest_CREATE
-	case sortTarget == "KEY":
-		sortByTarget = pb.RangeRequest_KEY
-	case sortTarget == "MODIFY":
-		sortByTarget = pb.RangeRequest_MOD
-	case sortTarget == "VALUE":
-		sortByTarget = pb.RangeRequest_VALUE
-	case sortTarget == "VERSION":
-		sortByTarget = pb.RangeRequest_VERSION
-	case sortTarget == "":
-		sortByTarget = pb.RangeRequest_KEY
-	default:
-		ExitWithError(ExitBadFeature, fmt.Errorf("bad sort target %v", rangeSortTarget))
-	}
-
-	req := &pb.RangeRequest{
-		Key:        key,
-		RangeEnd:   rangeEnd,
-		SortOrder:  sortByOrder,
-		SortTarget: sortByTarget,
-		Limit:      int64(rangeLimit),
-	}
-	resp, err := mustClientFromCmd(cmd).KV.Range(context.Background(), req)
-	if err != nil {
-		ExitWithError(ExitError, err)
-	}
-	for _, kv := range resp.Kvs {
-		fmt.Printf("%s %s\n", string(kv.Key), string(kv.Value))
-	}
-}

+ 1 - 1
etcdctlv3/main.go

@@ -48,7 +48,7 @@ func init() {
 	rootCmd.PersistentFlags().StringVar(&globalFlags.TLS.CAFile, "cacert", "", "verify certificates of TLS-enabled secure servers using this CA bundle")
 
 	rootCmd.AddCommand(
-		command.NewRangeCommand(),
+		command.NewGetCommand(),
 		command.NewPutCommand(),
 		command.NewDelCommand(),
 		command.NewTxnCommand(),