From d974e7a6188f377ec665ecc3c6e4587e8fc606c2 Mon Sep 17 00:00:00 2001 From: Gyuho Lee Date: Thu, 3 May 2018 13:25:37 -0700 Subject: [PATCH] etcdctl/ctlv3: fix watch with exec commands Following command was failing because the parser incorrectly picks up the second "watch" string in exec command, thus passing wrong exec commands. ``` ETCDCTL_API=3 ./bin/etcdctl watch aaa -- echo watch event received panic: runtime error: slice bounds out of range goroutine 1 [running]: github.com/coreos/etcd/etcdctl/ctlv3/command.parseWatchArgs(0xc42002e080, 0x8, 0x8, 0xc420206a20, 0x5, 0x6, 0x0, 0x0, 0x0, 0x0, ...) /home/gyuho/go/src/github.com/coreos/etcd/etcdctl/ctlv3/command/watch_command.go:303 +0xbed github.com/coreos/etcd/etcdctl/ctlv3/command.watchCommandFunc(0xc4202a7180, 0xc420206a20, 0x5, 0x6) /home/gyuho/go/src/github.com/coreos/etcd/etcdctl/ctlv3/command/watch_command.go:73 +0x11d github.com/coreos/etcd/vendor/github.com/spf13/cobra.(*Command).execute(0xc4202a7180, 0xc420206960, 0x6, 0x6, 0xc4202a7180, 0xc420206960) /home/gyuho/go/src/github.com/coreos/etcd/vendor/github.com/spf13/cobra/command.go:766 +0x2c1 github.com/coreos/etcd/vendor/github.com/spf13/cobra.(*Command).ExecuteC(0x1363de0, 0xc420128638, 0xc420185e01, 0xc420185ee8) /home/gyuho/go/src/github.com/coreos/etcd/vendor/github.com/spf13/cobra/command.go:852 +0x30a github.com/coreos/etcd/vendor/github.com/spf13/cobra.(*Command).Execute(0x1363de0, 0x0, 0x0) /home/gyuho/go/src/github.com/coreos/etcd/vendor/github.com/spf13/cobra/command.go:800 +0x2b github.com/coreos/etcd/etcdctl/ctlv3.Start() /home/gyuho/go/src/github.com/coreos/etcd/etcdctl/ctlv3/ctl_nocov.go:25 +0x8e main.main() /home/gyuho/go/src/github.com/coreos/etcd/etcdctl/main.go:40 +0x17b ``` Signed-off-by: Gyuho Lee --- etcdctl/ctlv3/command/watch_command.go | 171 +++++--- etcdctl/ctlv3/command/watch_command_test.go | 445 ++++++++++++++------ 2 files changed, 428 insertions(+), 188 deletions(-) diff --git a/etcdctl/ctlv3/command/watch_command.go b/etcdctl/ctlv3/command/watch_command.go index 33447f649eec..1a2cc4213f5b 100644 --- a/etcdctl/ctlv3/command/watch_command.go +++ b/etcdctl/ctlv3/command/watch_command.go @@ -179,118 +179,159 @@ func printWatchCh(c *clientv3.Client, ch clientv3.WatchChan, execArgs []string) // "--" characters are invalid arguments for "spf13/cobra" library, // so no need to handle such cases. func parseWatchArgs(osArgs, commandArgs []string, envKey, envRange string, interactive bool) (watchArgs []string, execArgs []string, err error) { - watchArgs = commandArgs + rawArgs := make([]string, len(osArgs)) + copy(rawArgs, osArgs) + watchArgs = make([]string, len(commandArgs)) + copy(watchArgs, commandArgs) - // remove preceding commands (e.g. "watch foo bar" in interactive mode) - idx := 0 - for idx = range watchArgs { - if watchArgs[idx] == "watch" { + // remove preceding commands (e.g. ./bin/etcdctl watch) + // handle "./bin/etcdctl watch foo -- echo watch event" + for idx := range rawArgs { + if rawArgs[idx] == "watch" { + rawArgs = rawArgs[idx+1:] break } } - if idx < len(watchArgs)-1 || envKey != "" { - if idx < len(watchArgs)-1 { - watchArgs = watchArgs[idx+1:] + + // remove preceding commands (e.g. "watch foo bar" in interactive mode) + // handle "./bin/etcdctl watch foo -- echo watch event" + if interactive { + if watchArgs[0] != "watch" { + // "watch" not found + watchPrefix, watchRev, watchPrevKey = false, 0, false + return nil, nil, errBadArgsInteractiveWatch } + watchArgs = watchArgs[1:] + } - execIdx, execExist := 0, false - for execIdx = range osArgs { - v := osArgs[execIdx] - if v == "--" && execIdx != len(osArgs)-1 { + execIdx, execExist := 0, false + if !interactive { + for execIdx = range rawArgs { + if rawArgs[execIdx] == "--" { 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{} - } + if execExist && execIdx == len(rawArgs)-1 { + // "watch foo bar --" should error + return nil, nil, errBadArgsNumSeparator } - - // "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-- + // "watch" with no argument should error + if !execExist && len(rawArgs) < 1 && envKey == "" { + return nil, nil, errBadArgsNum + } + if execExist && envKey != "" { + // "ETCDCTL_WATCH_KEY=foo watch foo -- echo 1" should error + // (watchArgs==["foo","echo","1"]) + widx, ridx := len(watchArgs)-1, len(rawArgs)-1 + for ; widx >= 0; widx-- { + if watchArgs[widx] == rawArgs[ridx] { + ridx-- continue } - if oidx == execIdx { // watchArgs has extra + // watchArgs has extra: + // ETCDCTL_WATCH_KEY=foo watch foo -- echo 1 + // watchArgs: foo echo 1 + if ridx == execIdx { return nil, nil, errBadArgsNumConflictEnv } } } - } else if interactive { // "watch" not found - return nil, nil, errBadArgsInteractiveWatch - } - if len(watchArgs) < 1 && envKey == "" { - return nil, nil, errBadArgsNum - } + // check conflicting arguments + // e.g. "watch --rev 1 -- echo Hello World" has no conflict + if !execExist && len(watchArgs) > 0 && envKey != "" { + // "ETCDCTL_WATCH_KEY=foo watch foo" should error + // (watchArgs==["foo"]) + return nil, nil, errBadArgsNumConflictEnv + } + } else { + for execIdx = range watchArgs { + if watchArgs[execIdx] == "--" { + execExist = true + break + } + } + if execExist && execIdx == len(watchArgs)-1 { + // "watch foo bar --" should error + watchPrefix, watchRev, watchPrevKey = false, 0, false + return nil, nil, errBadArgsNumSeparator + } - // remove preceding commands (e.g. ./bin/etcdctl watch) - for idx = range osArgs { - if osArgs[idx] == "watch" { - break + flagset := NewWatchCommand().Flags() + if err := flagset.Parse(watchArgs); err != nil { + watchPrefix, watchRev, watchPrevKey = false, 0, false + return nil, nil, err + } + pArgs := flagset.Args() + + // "watch" with no argument should error + if !execExist && envKey == "" && len(pArgs) < 1 { + watchPrefix, watchRev, watchPrevKey = false, 0, false + return nil, nil, errBadArgsNum + } + // check conflicting arguments + // e.g. "watch --rev 1 -- echo Hello World" has no conflict + if !execExist && len(pArgs) > 0 && envKey != "" { + // "ETCDCTL_WATCH_KEY=foo watch foo" should error + // (watchArgs==["foo"]) + watchPrefix, watchRev, watchPrevKey = false, 0, false + return nil, nil, errBadArgsNumConflictEnv } - } - if idx < len(osArgs)-1 { - osArgs = osArgs[idx+1:] - } else if envKey == "" { - return nil, nil, errBadArgsNum } - argsWithSep := osArgs - if interactive { // interactive mode pass "--" to the command args + argsWithSep := rawArgs + if interactive { + // interactive mode directly passes "--" to the command args argsWithSep = watchArgs } - foundSep := false + + idx, foundSep := 0, false for idx = range argsWithSep { if argsWithSep[idx] == "--" { foundSep = true break } } + if foundSep { + execArgs = argsWithSep[idx+1:] + } + if interactive { flagset := NewWatchCommand().Flags() if err := flagset.Parse(argsWithSep); err != nil { return nil, nil, err } watchArgs = flagset.Args() + + watchPrefix, err = flagset.GetBool("prefix") + if err != nil { + return nil, nil, err + } + watchRev, err = flagset.GetInt64("rev") + if err != nil { + return nil, nil, err + } + watchPrevKey, err = flagset.GetBool("prev-kv") + if err != nil { + return nil, nil, err + } } - // "watch -- echo hello" with ETCDCTL_WATCH_KEY=foo - // should be translated to "watch foo -- echo hello" + // "ETCDCTL_WATCH_KEY=foo watch -- echo hello" + // should translate "watch foo -- echo hello" // (watchArgs=["echo","hello"] should be ["foo","echo","hello"]) if envKey != "" { - tmp := []string{envKey} + ranges := []string{envKey} if envRange != "" { - tmp = append(tmp, envRange) + ranges = append(ranges, envRange) } - watchArgs = append(tmp, watchArgs...) + watchArgs = append(ranges, watchArgs...) } if !foundSep { return watchArgs, nil, nil } - if idx == len(argsWithSep)-1 { - // "watch foo bar --" should error - return nil, nil, errBadArgsNumSeparator - } - execArgs = argsWithSep[idx+1:] - // "watch foo bar --rev 1 -- echo hello" or "watch foo --rev 1 bar -- echo hello", // then "watchArgs" is "foo bar echo hello" // so need ignore args after "argsWithSep[idx]", which is "--" diff --git a/etcdctl/ctlv3/command/watch_command_test.go b/etcdctl/ctlv3/command/watch_command_test.go index 1456c971f0c8..2292deadcbba 100644 --- a/etcdctl/ctlv3/command/watch_command_test.go +++ b/etcdctl/ctlv3/command/watch_command_test.go @@ -26,6 +26,10 @@ func Test_parseWatchArgs(t *testing.T) { envKey, envRange string interactive bool + interactiveWatchPrefix bool + interactiveWatchRev int64 + interactiveWatchPrevKey bool + watchArgs []string execArgs []string err error @@ -145,6 +149,14 @@ func Test_parseWatchArgs(t *testing.T) { execArgs: []string{"echo", "Hello", "World"}, err: nil, }, + { + osArgs: []string{"./bin/etcdctl", "watch", "foo", "--", "echo", "watch", "event", "received"}, + commandArgs: []string{"foo", "echo", "watch", "event", "received"}, + interactive: false, + watchArgs: []string{"foo"}, + execArgs: []string{"echo", "watch", "event", "received"}, + err: nil, + }, { osArgs: []string{"./bin/etcdctl", "watch", "foo", "--rev", "1", "--", "echo", "Hello", "World"}, commandArgs: []string{"foo", "echo", "Hello", "World"}, @@ -153,6 +165,22 @@ func Test_parseWatchArgs(t *testing.T) { execArgs: []string{"echo", "Hello", "World"}, err: nil, }, + { + osArgs: []string{"./bin/etcdctl", "watch", "foo", "--rev", "1", "--", "echo", "watch", "event", "received"}, + commandArgs: []string{"foo", "echo", "watch", "event", "received"}, + interactive: false, + watchArgs: []string{"foo"}, + execArgs: []string{"echo", "watch", "event", "received"}, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "foo", "--", "echo", "watch", "event", "received"}, + commandArgs: []string{"foo", "echo", "watch", "event", "received"}, + interactive: false, + watchArgs: []string{"foo"}, + execArgs: []string{"echo", "watch", "event", "received"}, + err: nil, + }, { osArgs: []string{"./bin/etcdctl", "watch", "foo", "bar", "--", "echo", "Hello", "World"}, commandArgs: []string{"foo", "bar", "echo", "Hello", "World"}, @@ -186,162 +214,322 @@ func Test_parseWatchArgs(t *testing.T) { err: nil, }, { - osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "--", "echo", "Hello", "World"}, - commandArgs: []string{"echo", "Hello", "World"}, - envKey: "foo", - envRange: "", + osArgs: []string{"./bin/etcdctl", "watch", "foo", "bar", "--rev", "1", "--", "echo", "watch", "event", "received"}, + commandArgs: []string{"foo", "bar", "echo", "watch", "event", "received"}, interactive: false, - watchArgs: []string{"foo"}, - execArgs: []string{"echo", "Hello", "World"}, + watchArgs: []string{"foo", "bar"}, + execArgs: []string{"echo", "watch", "event", "received"}, err: nil, }, { - osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "--", "echo", "Hello", "World"}, - commandArgs: []string{"echo", "Hello", "World"}, - envKey: "foo", - envRange: "bar", + osArgs: []string{"./bin/etcdctl", "watch", "foo", "--rev", "1", "bar", "--", "echo", "Hello", "World"}, + commandArgs: []string{"foo", "bar", "echo", "Hello", "World"}, 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"}, + osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "foo", "bar", "--", "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"}, - interactive: true, - watchArgs: nil, - execArgs: nil, - err: errBadArgsInteractiveWatch, - }, - { - osArgs: []string{"./bin/etcdctl", "watch", "-i"}, - commandArgs: []string{"watch", "foo"}, - interactive: true, - watchArgs: []string{"foo"}, - execArgs: nil, - err: nil, - }, - { - osArgs: []string{"./bin/etcdctl", "watch", "-i"}, - commandArgs: []string{"watch", "foo", "bar"}, - interactive: true, watchArgs: []string{"foo", "bar"}, - 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"}, - interactive: true, - watchArgs: []string{"foo"}, - execArgs: nil, - err: nil, - }, - { - osArgs: []string{"./bin/etcdctl", "watch", "-i"}, - commandArgs: []string{"watch", "foo", "--rev", "1", "--", "echo", "Hello", "World"}, - interactive: true, - watchArgs: []string{"foo"}, execArgs: []string{"echo", "Hello", "World"}, err: nil, }, { - osArgs: []string{"./bin/etcdctl", "watch", "-i"}, - commandArgs: []string{"watch", "--rev", "1", "foo", "--", "echo", "Hello", "World"}, - interactive: true, - watchArgs: []string{"foo"}, - execArgs: []string{"echo", "Hello", "World"}, - err: nil, - }, - { - osArgs: []string{"./bin/etcdctl", "watch", "-i"}, - commandArgs: []string{"watch", "--", "echo", "Hello", "World"}, + osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "--", "echo", "Hello", "World"}, + commandArgs: []string{"echo", "Hello", "World"}, envKey: "foo", - interactive: true, + envRange: "", + interactive: false, watchArgs: []string{"foo"}, execArgs: []string{"echo", "Hello", "World"}, err: nil, }, { - osArgs: []string{"./bin/etcdctl", "watch", "-i"}, - commandArgs: []string{"watch", "--", "echo", "Hello", "World"}, + osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "--", "echo", "Hello", "World"}, + commandArgs: []string{"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"}, - interactive: true, - watchArgs: []string{"foo", "bar"}, - execArgs: []string{"echo", "Hello", "World"}, - err: nil, - }, - { - osArgs: []string{"./bin/etcdctl", "watch", "-i"}, - commandArgs: []string{"watch", "--rev", "1", "foo", "bar", "--", "echo", "Hello", "World"}, - interactive: true, + interactive: false, watchArgs: []string{"foo", "bar"}, execArgs: []string{"echo", "Hello", "World"}, err: nil, }, { - osArgs: []string{"./bin/etcdctl", "watch", "-i"}, - commandArgs: []string{"watch", "--rev", "1", "--", "echo", "Hello", "World"}, + osArgs: []string{"./bin/etcdctl", "watch", "foo", "bar", "--rev", "1", "--", "echo", "Hello", "World"}, + commandArgs: []string{"foo", "bar", "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"}, - interactive: true, - watchArgs: []string{"foo", "bar"}, - execArgs: []string{"echo", "Hello", "World"}, - err: nil, + interactive: false, + watchArgs: nil, + execArgs: nil, + err: errBadArgsNumConflictEnv, }, { - osArgs: []string{"./bin/etcdctl", "watch", "-i"}, - commandArgs: []string{"watch", "foo", "bar", "--rev", "1", "--", "echo", "Hello", "World"}, - interactive: true, - watchArgs: []string{"foo", "bar"}, - execArgs: []string{"echo", "Hello", "World"}, - err: nil, + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"foo", "bar", "--", "echo", "Hello", "World"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 0, + interactiveWatchPrevKey: false, + watchArgs: nil, + execArgs: nil, + err: errBadArgsInteractiveWatch, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "foo"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 0, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo"}, + execArgs: nil, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "foo", "bar"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 0, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo", "bar"}, + execArgs: nil, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch"}, + envKey: "foo", + envRange: "bar", + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 0, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo", "bar"}, + execArgs: nil, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch"}, + envKey: "hello world!", + envRange: "bar", + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 0, + interactiveWatchPrevKey: false, + watchArgs: []string{"hello world!", "bar"}, + execArgs: nil, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "foo", "--rev", "1"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 1, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo"}, + execArgs: nil, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "foo", "--rev", "1", "--", "echo", "Hello", "World"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 1, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo"}, + execArgs: []string{"echo", "Hello", "World"}, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "--rev", "1", "foo", "--", "echo", "Hello", "World"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 1, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo"}, + execArgs: []string{"echo", "Hello", "World"}, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "--rev", "5", "--prev-kv", "foo", "--", "echo", "Hello", "World"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 5, + interactiveWatchPrevKey: true, + watchArgs: []string{"foo"}, + execArgs: []string{"echo", "Hello", "World"}, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "--rev", "1"}, + envKey: "foo", + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 1, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo"}, + execArgs: nil, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "--rev", "1"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 0, + interactiveWatchPrevKey: false, + watchArgs: nil, + execArgs: nil, + err: errBadArgsNum, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "--rev", "1", "--prefix"}, + envKey: "foo", + interactive: true, + interactiveWatchPrefix: true, + interactiveWatchRev: 1, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo"}, + execArgs: nil, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "--rev", "100", "--prefix", "--prev-kv"}, + envKey: "foo", + interactive: true, + interactiveWatchPrefix: true, + interactiveWatchRev: 100, + interactiveWatchPrevKey: true, + watchArgs: []string{"foo"}, + execArgs: nil, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "--rev", "1", "--prefix"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 0, + interactiveWatchPrevKey: false, + watchArgs: nil, + execArgs: nil, + err: errBadArgsNum, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "--", "echo", "Hello", "World"}, + envKey: "foo", + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 0, + interactiveWatchPrevKey: false, + 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, + interactiveWatchPrefix: false, + interactiveWatchRev: 0, + interactiveWatchPrevKey: false, + 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"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 0, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo", "bar"}, + execArgs: []string{"echo", "Hello", "World"}, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "--rev", "1", "foo", "bar", "--", "echo", "Hello", "World"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 1, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo", "bar"}, + 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, + interactiveWatchPrefix: false, + interactiveWatchRev: 1, + interactiveWatchPrevKey: false, + 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"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 1, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo", "bar"}, + execArgs: []string{"echo", "Hello", "World"}, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "foo", "bar", "--rev", "1", "--", "echo", "Hello", "World"}, + interactive: true, + interactiveWatchPrefix: false, + interactiveWatchRev: 1, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo", "bar"}, + execArgs: []string{"echo", "Hello", "World"}, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "foo", "bar", "--rev", "7", "--prefix", "--", "echo", "Hello", "World"}, + interactive: true, + interactiveWatchPrefix: true, + interactiveWatchRev: 7, + interactiveWatchPrevKey: false, + watchArgs: []string{"foo", "bar"}, + execArgs: []string{"echo", "Hello", "World"}, + err: nil, + }, + { + osArgs: []string{"./bin/etcdctl", "watch", "-i"}, + commandArgs: []string{"watch", "foo", "bar", "--rev", "7", "--prefix", "--prev-kv", "--", "echo", "Hello", "World"}, + interactive: true, + interactiveWatchPrefix: true, + interactiveWatchRev: 7, + interactiveWatchPrevKey: true, + watchArgs: []string{"foo", "bar"}, + execArgs: []string{"echo", "Hello", "World"}, + err: nil, }, } for i, ts := range tt { @@ -355,5 +543,16 @@ func Test_parseWatchArgs(t *testing.T) { if !reflect.DeepEqual(execArgs, ts.execArgs) { t.Fatalf("#%d: execArgs expected %q, got %v", i, ts.execArgs, execArgs) } + if ts.interactive { + if ts.interactiveWatchPrefix != watchPrefix { + t.Fatalf("#%d: interactive watchPrefix expected %v, got %v", i, ts.interactiveWatchPrefix, watchPrefix) + } + if ts.interactiveWatchRev != watchRev { + t.Fatalf("#%d: interactive watchRev expected %d, got %d", i, ts.interactiveWatchRev, watchRev) + } + if ts.interactiveWatchPrevKey != watchPrevKey { + t.Fatalf("#%d: interactive watchPrevKey expected %v, got %v", i, ts.interactiveWatchPrevKey, watchPrevKey) + } + } } }