mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
etcdctlv3: better watch command
This commit is contained in:
parent
a22bb7a4e8
commit
755a022fcf
@ -108,3 +108,41 @@ OK
|
|||||||
1
|
1
|
||||||
./etcdctl range foo
|
./etcdctl range foo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### WATCH [options] [key or prefix]
|
||||||
|
|
||||||
|
Watch watches events stream on keys or prefixes. The watch command runs until it encounters an error or is terminated by the user.
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
- hex -- print out key and value as hex encode string
|
||||||
|
|
||||||
|
- i -- begins an interactive watch session
|
||||||
|
|
||||||
|
- prefix -- watch on a prefix if prefix is set.
|
||||||
|
|
||||||
|
- rev -- the revision to start watching. Specifying a revision is useful for observing past events.
|
||||||
|
|
||||||
|
#### Return value
|
||||||
|
|
||||||
|
Simple reply
|
||||||
|
|
||||||
|
- \<event\>\<key\>\r\n\<value\>\r\n\<event\>\<next_key\>\r\n\<next_value\>...
|
||||||
|
|
||||||
|
- Additional error string if WATCH failed. Exit code is non-zero.
|
||||||
|
|
||||||
|
TODO: probably json and binary encoded proto
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
./etcdctl watch foo
|
||||||
|
PUT
|
||||||
|
foo
|
||||||
|
bar
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Notes
|
||||||
|
|
||||||
|
TODO: doc interactive mode
|
@ -15,7 +15,6 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -104,22 +103,6 @@ func getCommandFunc(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, kv := range resp.Kvs {
|
for _, kv := range resp.Kvs {
|
||||||
k, v := string(kv.Key), string(kv.Value)
|
printKV(getHex, kv)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
42
etcdctlv3/command/util.go
Normal file
42
etcdctlv3/command/util.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
|
pb "github.com/coreos/etcd/storage/storagepb"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printKV(isHex bool, kv *pb.KeyValue) {
|
||||||
|
k, v := string(kv.Key), string(kv.Value)
|
||||||
|
if isHex {
|
||||||
|
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)
|
||||||
|
}
|
@ -24,20 +24,67 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/spf13/cobra"
|
"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/Godeps/_workspace/src/golang.org/x/net/context"
|
||||||
|
"github.com/coreos/etcd/clientv3"
|
||||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
watchRev int64
|
||||||
|
watchPrefix bool
|
||||||
|
watchHex bool
|
||||||
|
watchInteractive bool
|
||||||
|
)
|
||||||
|
|
||||||
// NewWatchCommand returns the cobra command for "watch".
|
// NewWatchCommand returns the cobra command for "watch".
|
||||||
func NewWatchCommand() *cobra.Command {
|
func NewWatchCommand() *cobra.Command {
|
||||||
return &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "watch",
|
Use: "watch [key or prefix]",
|
||||||
Short: "Watch watches the events happening or happened.",
|
Short: "Watch watches events stream on keys or prefixes.",
|
||||||
Run: watchCommandFunc,
|
Run: watchCommandFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd.Flags().BoolVar(&watchHex, "hex", false, "print out key and value as hex encode string for text format")
|
||||||
|
cmd.Flags().BoolVar(&watchInteractive, "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")
|
||||||
|
|
||||||
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// watchCommandFunc executes the "watch" command.
|
// watchCommandFunc executes the "watch" command.
|
||||||
func watchCommandFunc(cmd *cobra.Command, args []string) {
|
func watchCommandFunc(cmd *cobra.Command, args []string) {
|
||||||
|
if watchInteractive {
|
||||||
|
watchInteractiveFunc(cmd, args)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) != 1 {
|
||||||
|
ExitWithError(ExitBadArgs, fmt.Errorf("watch in non-interactive mode requires an argument as key or prefix"))
|
||||||
|
}
|
||||||
|
|
||||||
|
c := mustClientFromCmd(cmd)
|
||||||
|
w := clientv3.NewWatcher(c)
|
||||||
|
|
||||||
|
var wc clientv3.WatchChan
|
||||||
|
if !watchPrefix {
|
||||||
|
wc = w.Watch(context.TODO(), args[0], watchRev)
|
||||||
|
} else {
|
||||||
|
wc = w.Watch(context.TODO(), args[0], watchRev)
|
||||||
|
}
|
||||||
|
for resp := range wc {
|
||||||
|
for _, e := range resp.Events {
|
||||||
|
fmt.Printf("%s\r\n", e.Type)
|
||||||
|
printKV(watchHex, e.Kv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := w.Close()
|
||||||
|
if err == nil {
|
||||||
|
ExitWithError(ExitInterrupted, fmt.Errorf("watch is canceled by the server"))
|
||||||
|
}
|
||||||
|
ExitWithError(ExitBadConnection, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func watchInteractiveFunc(cmd *cobra.Command, args []string) {
|
||||||
wStream, err := mustClientFromCmd(cmd).Watch.Watch(context.TODO())
|
wStream, err := mustClientFromCmd(cmd).Watch.Watch(context.TODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ExitWithError(ExitBadConnection, err)
|
ExitWithError(ExitBadConnection, err)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user