Merge pull request #9142 from gyuho/watch-env

ctlv3: support ETCDCTL_WATCH_KEY, ETCDCTL_WATCH_RANGE_END
This commit is contained in:
Gyuho Lee 2018-01-17 12:39:49 -08:00 committed by GitHub
commit 30511860a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 397 additions and 49 deletions

View File

@ -15,6 +15,7 @@
package e2e
import (
"os"
"strings"
"testing"
)
@ -45,55 +46,94 @@ type kvExec struct {
func watchTest(cx ctlCtx) {
tests := []struct {
puts []kv
args []string
puts []kv
envKey string
envRange string
args []string
wkv []kvExec
}{
{ // watch 1 key
[]kv{{"sample", "value"}},
[]string{"sample", "--rev", "1"},
[]kvExec{{key: "sample", val: "value"}},
puts: []kv{{"sample", "value"}},
args: []string{"sample", "--rev", "1"},
wkv: []kvExec{{key: "sample", val: "value"}},
},
{ // watch 1 key with env
puts: []kv{{"sample", "value"}},
envKey: "sample",
args: []string{"--rev", "1"},
wkv: []kvExec{{key: "sample", val: "value"}},
},
{ // watch 1 key with "echo watch event received"
[]kv{{"sample", "value"}},
[]string{"sample", "--rev", "1", "--", "echo", "watch event received"},
[]kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
puts: []kv{{"sample", "value"}},
args: []string{"sample", "--rev", "1", "--", "echo", "watch event received"},
wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
},
{ // watch 1 key with ${ETCD_WATCH_VALUE}
puts: []kv{{"sample", "value"}},
args: []string{"sample", "--rev", "1", "--", "env"},
wkv: []kvExec{{key: "sample", val: "value", execOutput: `ETCD_WATCH_VALUE="value"`}},
},
{ // watch 1 key with "echo watch event received", with env
puts: []kv{{"sample", "value"}},
envKey: "sample",
args: []string{"--rev", "1", "--", "echo", "watch event received"},
wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
},
{ // watch 1 key with "echo watch event received"
[]kv{{"sample", "value"}},
[]string{"--rev", "1", "sample", "--", "echo", "watch event received"},
[]kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
puts: []kv{{"sample", "value"}},
args: []string{"--rev", "1", "sample", "--", "echo", "watch event received"},
wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
},
{ // watch 1 key with "echo \"Hello World!\""
[]kv{{"sample", "value"}},
[]string{"--rev", "1", "sample", "--", "echo", "\"Hello World!\""},
[]kvExec{{key: "sample", val: "value", execOutput: "Hello World!"}},
puts: []kv{{"sample", "value"}},
args: []string{"--rev", "1", "sample", "--", "echo", "\"Hello World!\""},
wkv: []kvExec{{key: "sample", val: "value", execOutput: "Hello World!"}},
},
{ // watch 1 key with "echo watch event received"
[]kv{{"sample", "value"}},
[]string{"sample", "samplx", "--rev", "1", "--", "echo", "watch event received"},
[]kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
puts: []kv{{"sample", "value"}},
args: []string{"sample", "samplx", "--rev", "1", "--", "echo", "watch event received"},
wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
},
{ // watch 1 key with "echo watch event received"
[]kv{{"sample", "value"}},
[]string{"sample", "--rev", "1", "samplx", "--", "echo", "watch event received"},
[]kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
puts: []kv{{"sample", "value"}},
envKey: "sample",
envRange: "samplx",
args: []string{"--rev", "1", "--", "echo", "watch event received"},
wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
},
{ // watch 1 key with "echo watch event received"
puts: []kv{{"sample", "value"}},
args: []string{"sample", "--rev", "1", "samplx", "--", "echo", "watch event received"},
wkv: []kvExec{{key: "sample", val: "value", execOutput: "watch event received"}},
},
{ // watch 3 keys by prefix
[]kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
[]string{"key", "--rev", "1", "--prefix"},
[]kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}, {key: "key3", val: "val3"}},
puts: []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
args: []string{"key", "--rev", "1", "--prefix"},
wkv: []kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}, {key: "key3", val: "val3"}},
},
{ // watch 3 keys by prefix, with env
puts: []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}},
envKey: "key",
args: []string{"--rev", "1", "--prefix"},
wkv: []kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}, {key: "key3", val: "val3"}},
},
{ // watch by revision
[]kv{{"etcd", "revision_1"}, {"etcd", "revision_2"}, {"etcd", "revision_3"}},
[]string{"etcd", "--rev", "2"},
[]kvExec{{key: "etcd", val: "revision_2"}, {key: "etcd", val: "revision_3"}},
puts: []kv{{"etcd", "revision_1"}, {"etcd", "revision_2"}, {"etcd", "revision_3"}},
args: []string{"etcd", "--rev", "2"},
wkv: []kvExec{{key: "etcd", val: "revision_2"}, {key: "etcd", val: "revision_3"}},
},
{ // watch 3 keys by range
[]kv{{"key1", "val1"}, {"key3", "val3"}, {"key2", "val2"}},
[]string{"key", "key3", "--rev", "1"},
[]kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}},
puts: []kv{{"key1", "val1"}, {"key3", "val3"}, {"key2", "val2"}},
args: []string{"key", "key3", "--rev", "1"},
wkv: []kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}},
},
{ // watch 3 keys by range, with env
puts: []kv{{"key1", "val1"}, {"key3", "val3"}, {"key2", "val2"}},
envKey: "key",
envRange: "key3",
args: []string{"--rev", "1"},
wkv: []kvExec{{key: "key1", val: "val1"}, {key: "key2", val: "val2"}},
},
}
@ -107,11 +147,30 @@ func watchTest(cx ctlCtx) {
}
close(donec)
}(i, tt.puts)
unsetEnv := func() {}
if tt.envKey != "" || tt.envRange != "" {
if tt.envKey != "" {
os.Setenv("ETCDCTL_WATCH_KEY", tt.envKey)
unsetEnv = func() { os.Unsetenv("ETCDCTL_WATCH_KEY") }
}
if tt.envRange != "" {
os.Setenv("ETCDCTL_WATCH_RANGE_END", tt.envRange)
unsetEnv = func() { os.Unsetenv("ETCDCTL_WATCH_RANGE_END") }
}
if tt.envKey != "" && tt.envRange != "" {
unsetEnv = func() {
os.Unsetenv("ETCDCTL_WATCH_KEY")
os.Unsetenv("ETCDCTL_WATCH_RANGE_END")
}
}
}
if err := ctlV3Watch(cx, tt.args, tt.wkv...); err != nil {
if cx.dialTimeout > 0 && !isGRPCTimedout(err) {
cx.t.Errorf("watchTest #%d: ctlV3Watch error (%v)", i, err)
}
}
unsetEnv()
<-donec
}
}

View File

@ -378,6 +378,13 @@ watch [options] <key or prefix>\n
# bar
```
```bash
ETCDCTL_WATCH_KEY=foo ./etcdctl watch
# PUT
# foo
# bar
```
Receive events and execute `echo watch event received`:
```bash
@ -388,6 +395,41 @@ Receive events and execute `echo watch event received`:
# watch event received
```
Watch response is set via `ETCD_WATCH_*` environmental variables:
```bash
./etcdctl watch foo -- sh -c "env | grep ETCD_WATCH_"
# PUT
# foo
# bar
# ETCD_WATCH_REVISION=11
# ETCD_WATCH_KEY="foo"
# ETCD_WATCH_EVENT_TYPE="PUT"
# ETCD_WATCH_VALUE="bar"
```
Watch with environmental variables and execute `echo watch event received`:
```bash
export ETCDCTL_WATCH_KEY=foo
./etcdctl watch -- echo watch event received
# PUT
# foo
# bar
# watch event received
```
```bash
export ETCDCTL_WATCH_KEY=foo
export ETCDCTL_WATCH_RANGE_END=foox
./etcdctl watch -- echo watch event received
# PUT
# fob
# bar
# watch event received
```
##### Interactive
```bash
@ -413,6 +455,29 @@ watch foo -- echo watch event received
# watch event received
```
Watch with environmental variables and execute `echo watch event received`:
```bash
export ETCDCTL_WATCH_KEY=foo
./etcdctl watch -i
watch -- echo watch event received
# PUT
# foo
# bar
# watch event received
```
```bash
export ETCDCTL_WATCH_KEY=foo
export ETCDCTL_WATCH_RANGE_END=foox
./etcdctl watch -i
watch -- echo watch event received
# PUT
# fob
# bar
# watch event received
```
### LEASE \<subcommand\>
LEASE provides commands for key lease management.

View File

@ -101,8 +101,19 @@ type clientConfig struct {
acfg *authCfg
}
type discardValue struct{}
func (*discardValue) String() string { return "" }
func (*discardValue) Set(string) error { return nil }
func (*discardValue) Type() string { return "" }
func clientConfigFromCmd(cmd *cobra.Command) *clientConfig {
fs := cmd.InheritedFlags()
// silence "pkg/flags: unrecognized environment variable ETCDCTL_WATCH_KEY=foo" warnings
// silence "pkg/flags: unrecognized environment variable ETCDCTL_WATCH_RANGE_END=bar" warnings
fs.AddFlag(&pflag.Flag{Name: "watch-key", Value: &discardValue{}})
fs.AddFlag(&pflag.Flag{Name: "watch-range-end", Value: &discardValue{}})
flags.SetPflagsFromEnv("ETCDCTL", fs)
debug, err := cmd.Flags().GetBool("debug")

View File

@ -30,6 +30,7 @@ import (
var (
errBadArgsNum = errors.New("bad number of arguments")
errBadArgsNumConflictEnv = errors.New("bad number of arguments (found conflicting environment key)")
errBadArgsNumSeparator = errors.New("bad number of arguments (found separator --, but no commands)")
errBadArgsInteractiveWatch = errors.New("args[0] must be 'watch' for interactive calls")
)
@ -59,12 +60,17 @@ func NewWatchCommand() *cobra.Command {
// watchCommandFunc executes the "watch" command.
func watchCommandFunc(cmd *cobra.Command, args []string) {
envKey, envRange := os.Getenv("ETCDCTL_WATCH_KEY"), os.Getenv("ETCDCTL_WATCH_RANGE_END")
if envKey == "" && envRange != "" {
ExitWithError(ExitBadArgs, fmt.Errorf("ETCDCTL_WATCH_KEY is empty but got ETCDCTL_WATCH_RANGE_END=%q", envRange))
}
if watchInteractive {
watchInteractiveFunc(cmd, os.Args)
watchInteractiveFunc(cmd, os.Args, envKey, envRange)
return
}
watchArgs, execArgs, err := parseWatchArgs(os.Args, args, false)
watchArgs, execArgs, err := parseWatchArgs(os.Args, args, envKey, envRange, false)
if err != nil {
ExitWithError(ExitBadArgs, err)
}
@ -82,7 +88,7 @@ func watchCommandFunc(cmd *cobra.Command, args []string) {
ExitWithError(ExitInterrupted, fmt.Errorf("watch is canceled by the server"))
}
func watchInteractiveFunc(cmd *cobra.Command, osArgs []string) {
func watchInteractiveFunc(cmd *cobra.Command, osArgs []string, envKey, envRange string) {
c := mustClientFromCmd(cmd)
reader := bufio.NewReader(os.Stdin)
@ -95,7 +101,7 @@ func watchInteractiveFunc(cmd *cobra.Command, osArgs []string) {
l = strings.TrimSuffix(l, "\n")
args := argify(l)
if len(args) < 2 {
if len(args) < 2 && envKey == "" {
fmt.Fprintf(os.Stderr, "Invalid command %s (command type or key is not provided)\n", l)
continue
}
@ -105,7 +111,7 @@ func watchInteractiveFunc(cmd *cobra.Command, osArgs []string) {
continue
}
watchArgs, execArgs, perr := parseWatchArgs(osArgs, args, true)
watchArgs, execArgs, perr := parseWatchArgs(osArgs, args, envKey, envRange, true)
if perr != nil {
ExitWithError(ExitBadArgs, perr)
}
@ -149,11 +155,18 @@ func printWatchCh(c *clientv3.Client, ch clientv3.WatchChan, execArgs []string)
display.Watch(resp)
if len(execArgs) > 0 {
cmd := exec.CommandContext(c.Ctx(), execArgs[0], execArgs[1:]...)
cmd.Env = os.Environ()
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "command %q error (%v)\n", execArgs, err)
for _, ev := range resp.Events {
cmd := exec.CommandContext(c.Ctx(), execArgs[0], execArgs[1:]...)
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_REVISION=%d", resp.Header.Revision))
cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_EVENT_TYPE=%q", ev.Type))
cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_KEY=%q", ev.Kv.Key))
cmd.Env = append(cmd.Env, fmt.Sprintf("ETCD_WATCH_VALUE=%q", ev.Kv.Value))
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "command %q error (%v)\n", execArgs, err)
os.Exit(1)
}
}
}
}
@ -165,7 +178,7 @@ func printWatchCh(c *clientv3.Client, ch clientv3.WatchChan, execArgs []string)
// (e.g. ./bin/etcdctl watch foo --rev 1 bar).
// "--" characters are invalid arguments for "spf13/cobra" library,
// so no need to handle such cases.
func parseWatchArgs(osArgs, commandArgs []string, interactive bool) (watchArgs []string, execArgs []string, err error) {
func parseWatchArgs(osArgs, commandArgs []string, envKey, envRange string, interactive bool) (watchArgs []string, execArgs []string, err error) {
watchArgs = commandArgs
// remove preceding commands (e.g. "watch foo bar" in interactive mode)
@ -175,12 +188,54 @@ func parseWatchArgs(osArgs, commandArgs []string, interactive bool) (watchArgs [
break
}
}
if idx < len(watchArgs)-1 {
watchArgs = watchArgs[idx+1:]
if idx < len(watchArgs)-1 || envKey != "" {
if idx < len(watchArgs)-1 {
watchArgs = watchArgs[idx+1:]
}
execIdx, execExist := 0, false
for execIdx = range osArgs {
v := osArgs[execIdx]
if v == "--" && execIdx != len(osArgs)-1 {
execExist = true
break
}
}
if idx == len(watchArgs)-1 && envKey != "" {
if len(watchArgs) > 0 && !interactive {
// "watch --rev 1 -- echo Hello World" has no conflict
if !execExist {
// "watch foo" with ETCDCTL_WATCH_KEY=foo
// (watchArgs==["foo"])
return nil, nil, errBadArgsNumConflictEnv
}
}
// otherwise, watch with no argument and environment key is set
// if interactive, first "watch" command string should be removed
if interactive {
watchArgs = []string{}
}
}
// "watch foo -- echo hello" with ETCDCTL_WATCH_KEY=foo
// (watchArgs==["foo","echo","hello"])
if envKey != "" && execExist {
widx, oidx := 0, len(osArgs)-1
for widx = len(watchArgs) - 1; widx >= 0; widx-- {
if watchArgs[widx] == osArgs[oidx] {
oidx--
continue
}
if oidx == execIdx { // watchArgs has extra
return nil, nil, errBadArgsNumConflictEnv
}
}
}
} else if interactive { // "watch" not found
return nil, nil, errBadArgsInteractiveWatch
}
if len(watchArgs) < 1 {
if len(watchArgs) < 1 && envKey == "" {
return nil, nil, errBadArgsNum
}
@ -192,7 +247,7 @@ func parseWatchArgs(osArgs, commandArgs []string, interactive bool) (watchArgs [
}
if idx < len(osArgs)-1 {
osArgs = osArgs[idx+1:]
} else {
} else if envKey == "" {
return nil, nil, errBadArgsNum
}
@ -202,7 +257,7 @@ func parseWatchArgs(osArgs, commandArgs []string, interactive bool) (watchArgs [
}
foundSep := false
for idx = range argsWithSep {
if argsWithSep[idx] == "--" && idx > 0 {
if argsWithSep[idx] == "--" {
foundSep = true
break
}
@ -214,6 +269,18 @@ func parseWatchArgs(osArgs, commandArgs []string, interactive bool) (watchArgs [
}
watchArgs = flagset.Args()
}
// "watch -- echo hello" with ETCDCTL_WATCH_KEY=foo
// should be translated to "watch foo -- echo hello"
// (watchArgs=["echo","hello"] should be ["foo","echo","hello"])
if envKey != "" {
tmp := []string{envKey}
if envRange != "" {
tmp = append(tmp, envRange)
}
watchArgs = append(tmp, watchArgs...)
}
if !foundSep {
return watchArgs, nil, nil
}

View File

@ -21,9 +21,10 @@ import (
func Test_parseWatchArgs(t *testing.T) {
tt := []struct {
osArgs []string // raw arguments to "watch" command
commandArgs []string // arguments after "spf13/cobra" preprocessing
interactive bool
osArgs []string // raw arguments to "watch" command
commandArgs []string // arguments after "spf13/cobra" preprocessing
envKey, envRange string
interactive bool
watchArgs []string
execArgs []string
@ -45,9 +46,66 @@ func Test_parseWatchArgs(t *testing.T) {
execArgs: nil,
err: errBadArgsNumSeparator,
},
{
osArgs: []string{"./bin/etcdctl", "watch"},
commandArgs: nil,
envKey: "foo",
envRange: "bar",
interactive: false,
watchArgs: []string{"foo", "bar"},
execArgs: nil,
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "foo"},
commandArgs: []string{"foo"},
envKey: "foo",
envRange: "",
interactive: false,
watchArgs: nil,
execArgs: nil,
err: errBadArgsNumConflictEnv,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "foo", "bar"},
commandArgs: []string{"foo", "bar"},
envKey: "foo",
envRange: "",
interactive: false,
watchArgs: nil,
execArgs: nil,
err: errBadArgsNumConflictEnv,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "foo", "bar"},
commandArgs: []string{"foo", "bar"},
envKey: "foo",
envRange: "bar",
interactive: false,
watchArgs: nil,
execArgs: nil,
err: errBadArgsNumConflictEnv,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "foo"},
commandArgs: []string{"foo"},
interactive: false,
watchArgs: []string{"foo"},
execArgs: nil,
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch"},
commandArgs: nil,
envKey: "foo",
interactive: false,
watchArgs: []string{"foo"},
execArgs: nil,
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "foo"},
commandArgs: []string{"foo"},
interactive: false,
watchArgs: []string{"foo"},
execArgs: nil,
@ -56,6 +114,16 @@ func Test_parseWatchArgs(t *testing.T) {
{
osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "foo"},
commandArgs: []string{"foo"},
envKey: "foo",
interactive: false,
watchArgs: nil,
execArgs: nil,
err: errBadArgsNumConflictEnv,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1"},
commandArgs: nil,
envKey: "foo",
interactive: false,
watchArgs: []string{"foo"},
execArgs: nil,
@ -117,6 +185,35 @@ func Test_parseWatchArgs(t *testing.T) {
execArgs: []string{"echo", "Hello", "World"},
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "--", "echo", "Hello", "World"},
commandArgs: []string{"echo", "Hello", "World"},
envKey: "foo",
envRange: "",
interactive: false,
watchArgs: []string{"foo"},
execArgs: []string{"echo", "Hello", "World"},
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "--", "echo", "Hello", "World"},
commandArgs: []string{"echo", "Hello", "World"},
envKey: "foo",
envRange: "bar",
interactive: false,
watchArgs: []string{"foo", "bar"},
execArgs: []string{"echo", "Hello", "World"},
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "foo", "bar", "--rev", "1", "--", "echo", "Hello", "World"},
commandArgs: []string{"foo", "bar", "echo", "Hello", "World"},
envKey: "foo",
interactive: false,
watchArgs: nil,
execArgs: nil,
err: errBadArgsNumConflictEnv,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
commandArgs: []string{"foo", "bar", "--", "echo", "Hello", "World"},
@ -141,6 +238,26 @@ func Test_parseWatchArgs(t *testing.T) {
execArgs: nil,
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
commandArgs: []string{"watch"},
envKey: "foo",
envRange: "bar",
interactive: true,
watchArgs: []string{"foo", "bar"},
execArgs: nil,
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
commandArgs: []string{"watch"},
envKey: "hello world!",
envRange: "bar",
interactive: true,
watchArgs: []string{"hello world!", "bar"},
execArgs: nil,
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
commandArgs: []string{"watch", "foo", "--rev", "1"},
@ -165,6 +282,25 @@ func Test_parseWatchArgs(t *testing.T) {
execArgs: []string{"echo", "Hello", "World"},
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
commandArgs: []string{"watch", "--", "echo", "Hello", "World"},
envKey: "foo",
interactive: true,
watchArgs: []string{"foo"},
execArgs: []string{"echo", "Hello", "World"},
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
commandArgs: []string{"watch", "--", "echo", "Hello", "World"},
envKey: "foo",
envRange: "bar",
interactive: true,
watchArgs: []string{"foo", "bar"},
execArgs: []string{"echo", "Hello", "World"},
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
commandArgs: []string{"watch", "foo", "bar", "--", "echo", "Hello", "World"},
@ -181,6 +317,16 @@ func Test_parseWatchArgs(t *testing.T) {
execArgs: []string{"echo", "Hello", "World"},
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
commandArgs: []string{"watch", "--rev", "1", "--", "echo", "Hello", "World"},
envKey: "foo",
envRange: "bar",
interactive: true,
watchArgs: []string{"foo", "bar"},
execArgs: []string{"echo", "Hello", "World"},
err: nil,
},
{
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
commandArgs: []string{"watch", "foo", "--rev", "1", "bar", "--", "echo", "Hello", "World"},
@ -199,7 +345,7 @@ func Test_parseWatchArgs(t *testing.T) {
},
}
for i, ts := range tt {
watchArgs, execArgs, err := parseWatchArgs(ts.osArgs, ts.commandArgs, ts.interactive)
watchArgs, execArgs, err := parseWatchArgs(ts.osArgs, ts.commandArgs, ts.envKey, ts.envRange, ts.interactive)
if err != ts.err {
t.Fatalf("#%d: error expected %v, got %v", i, ts.err, err)
}