Merge pull request #9688 from gyuho/fix-watch
etcdctl/ctlv3: fix watch with exec commands, interactive mode flag parsingrelease-3.4
commit
14a2d0d54d
|
@ -9,6 +9,7 @@
|
||||||
*.log
|
*.log
|
||||||
/etcd
|
/etcd
|
||||||
/hack/insta-discovery/.env
|
/hack/insta-discovery/.env
|
||||||
|
*.coverprofile
|
||||||
*.test
|
*.test
|
||||||
hack/tls-setup/certs
|
hack/tls-setup/certs
|
||||||
.idea
|
.idea
|
||||||
|
|
3
Makefile
3
Makefile
|
@ -20,9 +20,12 @@ clean:
|
||||||
rm -f ./codecov
|
rm -f ./codecov
|
||||||
rm -rf ./agent-*
|
rm -rf ./agent-*
|
||||||
rm -rf ./covdir
|
rm -rf ./covdir
|
||||||
|
rm -f ./*.coverprofile
|
||||||
rm -f ./*.log
|
rm -f ./*.log
|
||||||
rm -f ./bin/Dockerfile-release
|
rm -f ./bin/Dockerfile-release
|
||||||
rm -rf ./bin/*.etcd
|
rm -rf ./bin/*.etcd
|
||||||
|
rm -rf ./default.etcd
|
||||||
|
rm -rf ./tests/e2e/default.etcd
|
||||||
rm -rf ./gopath
|
rm -rf ./gopath
|
||||||
rm -rf ./gopath.proto
|
rm -rf ./gopath.proto
|
||||||
rm -rf ./release
|
rm -rf ./release
|
||||||
|
|
|
@ -179,118 +179,159 @@ func printWatchCh(c *clientv3.Client, ch clientv3.WatchChan, execArgs []string)
|
||||||
// "--" characters are invalid arguments for "spf13/cobra" library,
|
// "--" characters are invalid arguments for "spf13/cobra" library,
|
||||||
// so no need to handle such cases.
|
// so no need to handle such cases.
|
||||||
func parseWatchArgs(osArgs, commandArgs []string, envKey, envRange 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
|
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)
|
// remove preceding commands (e.g. ./bin/etcdctl watch)
|
||||||
idx := 0
|
// handle "./bin/etcdctl watch foo -- echo watch event"
|
||||||
for idx = range watchArgs {
|
for idx := range rawArgs {
|
||||||
if watchArgs[idx] == "watch" {
|
if rawArgs[idx] == "watch" {
|
||||||
|
rawArgs = rawArgs[idx+1:]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if idx < len(watchArgs)-1 || envKey != "" {
|
|
||||||
if idx < len(watchArgs)-1 {
|
|
||||||
watchArgs = watchArgs[idx+1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
execIdx, execExist := 0, false
|
// remove preceding commands (e.g. "watch foo bar" in interactive mode)
|
||||||
for execIdx = range osArgs {
|
// handle "./bin/etcdctl watch foo -- echo watch event"
|
||||||
v := osArgs[execIdx]
|
if interactive {
|
||||||
if v == "--" && execIdx != len(osArgs)-1 {
|
if watchArgs[0] != "watch" {
|
||||||
|
// "watch" not found
|
||||||
|
watchPrefix, watchRev, watchPrevKey = false, 0, false
|
||||||
|
return nil, nil, errBadArgsInteractiveWatch
|
||||||
|
}
|
||||||
|
watchArgs = watchArgs[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
execIdx, execExist := 0, false
|
||||||
|
if !interactive {
|
||||||
|
for execIdx = range rawArgs {
|
||||||
|
if rawArgs[execIdx] == "--" {
|
||||||
execExist = true
|
execExist = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if execExist && execIdx == len(rawArgs)-1 {
|
||||||
if idx == len(watchArgs)-1 && envKey != "" {
|
// "watch foo bar --" should error
|
||||||
if len(watchArgs) > 0 && !interactive {
|
return nil, nil, errBadArgsNumSeparator
|
||||||
// "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" with no argument should error
|
||||||
// "watch foo -- echo hello" with ETCDCTL_WATCH_KEY=foo
|
if !execExist && len(rawArgs) < 1 && envKey == "" {
|
||||||
// (watchArgs==["foo","echo","hello"])
|
return nil, nil, errBadArgsNum
|
||||||
if envKey != "" && execExist {
|
}
|
||||||
widx, oidx := 0, len(osArgs)-1
|
if execExist && envKey != "" {
|
||||||
for widx = len(watchArgs) - 1; widx >= 0; widx-- {
|
// "ETCDCTL_WATCH_KEY=foo watch foo -- echo 1" should error
|
||||||
if watchArgs[widx] == osArgs[oidx] {
|
// (watchArgs==["foo","echo","1"])
|
||||||
oidx--
|
widx, ridx := len(watchArgs)-1, len(rawArgs)-1
|
||||||
|
for ; widx >= 0; widx-- {
|
||||||
|
if watchArgs[widx] == rawArgs[ridx] {
|
||||||
|
ridx--
|
||||||
continue
|
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
|
return nil, nil, errBadArgsNumConflictEnv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if interactive { // "watch" not found
|
// check conflicting arguments
|
||||||
return nil, nil, errBadArgsInteractiveWatch
|
// e.g. "watch --rev 1 -- echo Hello World" has no conflict
|
||||||
}
|
if !execExist && len(watchArgs) > 0 && envKey != "" {
|
||||||
if len(watchArgs) < 1 && envKey == "" {
|
// "ETCDCTL_WATCH_KEY=foo watch foo" should error
|
||||||
return nil, nil, errBadArgsNum
|
// (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)
|
flagset := NewWatchCommand().Flags()
|
||||||
for idx = range osArgs {
|
if err := flagset.Parse(watchArgs); err != nil {
|
||||||
if osArgs[idx] == "watch" {
|
watchPrefix, watchRev, watchPrevKey = false, 0, false
|
||||||
break
|
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
|
argsWithSep := rawArgs
|
||||||
if interactive { // interactive mode pass "--" to the command args
|
if interactive {
|
||||||
|
// interactive mode directly passes "--" to the command args
|
||||||
argsWithSep = watchArgs
|
argsWithSep = watchArgs
|
||||||
}
|
}
|
||||||
foundSep := false
|
|
||||||
|
idx, foundSep := 0, false
|
||||||
for idx = range argsWithSep {
|
for idx = range argsWithSep {
|
||||||
if argsWithSep[idx] == "--" {
|
if argsWithSep[idx] == "--" {
|
||||||
foundSep = true
|
foundSep = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if foundSep {
|
||||||
|
execArgs = argsWithSep[idx+1:]
|
||||||
|
}
|
||||||
|
|
||||||
if interactive {
|
if interactive {
|
||||||
flagset := NewWatchCommand().Flags()
|
flagset := NewWatchCommand().Flags()
|
||||||
if err := flagset.Parse(argsWithSep); err != nil {
|
if err := flagset.Parse(argsWithSep); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
watchArgs = flagset.Args()
|
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
|
// "ETCDCTL_WATCH_KEY=foo watch -- echo hello"
|
||||||
// should be translated to "watch foo -- echo hello"
|
// should translate "watch foo -- echo hello"
|
||||||
// (watchArgs=["echo","hello"] should be ["foo","echo","hello"])
|
// (watchArgs=["echo","hello"] should be ["foo","echo","hello"])
|
||||||
if envKey != "" {
|
if envKey != "" {
|
||||||
tmp := []string{envKey}
|
ranges := []string{envKey}
|
||||||
if envRange != "" {
|
if envRange != "" {
|
||||||
tmp = append(tmp, envRange)
|
ranges = append(ranges, envRange)
|
||||||
}
|
}
|
||||||
watchArgs = append(tmp, watchArgs...)
|
watchArgs = append(ranges, watchArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !foundSep {
|
if !foundSep {
|
||||||
return watchArgs, nil, nil
|
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",
|
// "watch foo bar --rev 1 -- echo hello" or "watch foo --rev 1 bar -- echo hello",
|
||||||
// then "watchArgs" is "foo bar echo hello"
|
// then "watchArgs" is "foo bar echo hello"
|
||||||
// so need ignore args after "argsWithSep[idx]", which is "--"
|
// so need ignore args after "argsWithSep[idx]", which is "--"
|
||||||
|
|
|
@ -26,6 +26,10 @@ func Test_parseWatchArgs(t *testing.T) {
|
||||||
envKey, envRange string
|
envKey, envRange string
|
||||||
interactive bool
|
interactive bool
|
||||||
|
|
||||||
|
interactiveWatchPrefix bool
|
||||||
|
interactiveWatchRev int64
|
||||||
|
interactiveWatchPrevKey bool
|
||||||
|
|
||||||
watchArgs []string
|
watchArgs []string
|
||||||
execArgs []string
|
execArgs []string
|
||||||
err error
|
err error
|
||||||
|
@ -145,6 +149,14 @@ func Test_parseWatchArgs(t *testing.T) {
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
execArgs: []string{"echo", "Hello", "World"},
|
||||||
err: nil,
|
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"},
|
osArgs: []string{"./bin/etcdctl", "watch", "foo", "--rev", "1", "--", "echo", "Hello", "World"},
|
||||||
commandArgs: []string{"foo", "echo", "Hello", "World"},
|
commandArgs: []string{"foo", "echo", "Hello", "World"},
|
||||||
|
@ -153,6 +165,22 @@ func Test_parseWatchArgs(t *testing.T) {
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
execArgs: []string{"echo", "Hello", "World"},
|
||||||
err: nil,
|
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"},
|
osArgs: []string{"./bin/etcdctl", "watch", "foo", "bar", "--", "echo", "Hello", "World"},
|
||||||
commandArgs: []string{"foo", "bar", "echo", "Hello", "World"},
|
commandArgs: []string{"foo", "bar", "echo", "Hello", "World"},
|
||||||
|
@ -185,6 +213,30 @@ func Test_parseWatchArgs(t *testing.T) {
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
execArgs: []string{"echo", "Hello", "World"},
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
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", "bar"},
|
||||||
|
execArgs: []string{"echo", "watch", "event", "received"},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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", "--rev", "1", "foo", "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", "--rev", "1", "--", "echo", "Hello", "World"},
|
osArgs: []string{"./bin/etcdctl", "watch", "--rev", "1", "--", "echo", "Hello", "World"},
|
||||||
commandArgs: []string{"echo", "Hello", "World"},
|
commandArgs: []string{"echo", "Hello", "World"},
|
||||||
|
@ -215,133 +267,269 @@ func Test_parseWatchArgs(t *testing.T) {
|
||||||
err: errBadArgsNumConflictEnv,
|
err: errBadArgsNumConflictEnv,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"foo", "bar", "--", "echo", "Hello", "World"},
|
commandArgs: []string{"foo", "bar", "--", "echo", "Hello", "World"},
|
||||||
interactive: true,
|
interactive: true,
|
||||||
watchArgs: nil,
|
interactiveWatchPrefix: false,
|
||||||
execArgs: nil,
|
interactiveWatchRev: 0,
|
||||||
err: errBadArgsInteractiveWatch,
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: nil,
|
||||||
|
execArgs: nil,
|
||||||
|
err: errBadArgsInteractiveWatch,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "foo"},
|
commandArgs: []string{"watch", "foo"},
|
||||||
interactive: true,
|
interactive: true,
|
||||||
watchArgs: []string{"foo"},
|
interactiveWatchPrefix: false,
|
||||||
execArgs: nil,
|
interactiveWatchRev: 0,
|
||||||
err: nil,
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: []string{"foo"},
|
||||||
|
execArgs: nil,
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "foo", "bar"},
|
commandArgs: []string{"watch", "foo", "bar"},
|
||||||
interactive: true,
|
interactive: true,
|
||||||
watchArgs: []string{"foo", "bar"},
|
interactiveWatchPrefix: false,
|
||||||
execArgs: nil,
|
interactiveWatchRev: 0,
|
||||||
err: nil,
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: []string{"foo", "bar"},
|
||||||
|
execArgs: nil,
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch"},
|
commandArgs: []string{"watch"},
|
||||||
envKey: "foo",
|
envKey: "foo",
|
||||||
envRange: "bar",
|
envRange: "bar",
|
||||||
interactive: true,
|
interactive: true,
|
||||||
watchArgs: []string{"foo", "bar"},
|
interactiveWatchPrefix: false,
|
||||||
execArgs: nil,
|
interactiveWatchRev: 0,
|
||||||
err: nil,
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: []string{"foo", "bar"},
|
||||||
|
execArgs: nil,
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch"},
|
commandArgs: []string{"watch"},
|
||||||
envKey: "hello world!",
|
envKey: "hello world!",
|
||||||
envRange: "bar",
|
envRange: "bar",
|
||||||
interactive: true,
|
interactive: true,
|
||||||
watchArgs: []string{"hello world!", "bar"},
|
interactiveWatchPrefix: false,
|
||||||
execArgs: nil,
|
interactiveWatchRev: 0,
|
||||||
err: nil,
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: []string{"hello world!", "bar"},
|
||||||
|
execArgs: nil,
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "foo", "--rev", "1"},
|
commandArgs: []string{"watch", "foo", "--rev", "1"},
|
||||||
interactive: true,
|
interactive: true,
|
||||||
watchArgs: []string{"foo"},
|
interactiveWatchPrefix: false,
|
||||||
execArgs: nil,
|
interactiveWatchRev: 1,
|
||||||
err: nil,
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: []string{"foo"},
|
||||||
|
execArgs: nil,
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "foo", "--rev", "1", "--", "echo", "Hello", "World"},
|
commandArgs: []string{"watch", "foo", "--rev", "1", "--", "echo", "Hello", "World"},
|
||||||
interactive: true,
|
interactive: true,
|
||||||
watchArgs: []string{"foo"},
|
interactiveWatchPrefix: false,
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
interactiveWatchRev: 1,
|
||||||
err: nil,
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: []string{"foo"},
|
||||||
|
execArgs: []string{"echo", "Hello", "World"},
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "--rev", "1", "foo", "--", "echo", "Hello", "World"},
|
commandArgs: []string{"watch", "--rev", "1", "foo", "--", "echo", "Hello", "World"},
|
||||||
interactive: true,
|
interactive: true,
|
||||||
watchArgs: []string{"foo"},
|
interactiveWatchPrefix: false,
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
interactiveWatchRev: 1,
|
||||||
err: nil,
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: []string{"foo"},
|
||||||
|
execArgs: []string{"echo", "Hello", "World"},
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "--", "echo", "Hello", "World"},
|
commandArgs: []string{"watch", "--rev", "5", "--prev-kv", "foo", "--", "echo", "Hello", "World"},
|
||||||
envKey: "foo",
|
interactive: true,
|
||||||
interactive: true,
|
interactiveWatchPrefix: false,
|
||||||
watchArgs: []string{"foo"},
|
interactiveWatchRev: 5,
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
interactiveWatchPrevKey: true,
|
||||||
err: nil,
|
watchArgs: []string{"foo"},
|
||||||
|
execArgs: []string{"echo", "Hello", "World"},
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "--", "echo", "Hello", "World"},
|
commandArgs: []string{"watch", "--rev", "1"},
|
||||||
envKey: "foo",
|
envKey: "foo",
|
||||||
envRange: "bar",
|
interactive: true,
|
||||||
interactive: true,
|
interactiveWatchPrefix: false,
|
||||||
watchArgs: []string{"foo", "bar"},
|
interactiveWatchRev: 1,
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
interactiveWatchPrevKey: false,
|
||||||
err: nil,
|
watchArgs: []string{"foo"},
|
||||||
|
execArgs: nil,
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "foo", "bar", "--", "echo", "Hello", "World"},
|
commandArgs: []string{"watch", "--rev", "1"},
|
||||||
interactive: true,
|
interactive: true,
|
||||||
watchArgs: []string{"foo", "bar"},
|
interactiveWatchPrefix: false,
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
interactiveWatchRev: 0,
|
||||||
err: nil,
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: nil,
|
||||||
|
execArgs: nil,
|
||||||
|
err: errBadArgsNum,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "--rev", "1", "foo", "bar", "--", "echo", "Hello", "World"},
|
commandArgs: []string{"watch", "--rev", "1", "--prefix"},
|
||||||
interactive: true,
|
envKey: "foo",
|
||||||
watchArgs: []string{"foo", "bar"},
|
interactive: true,
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
interactiveWatchPrefix: true,
|
||||||
err: nil,
|
interactiveWatchRev: 1,
|
||||||
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: []string{"foo"},
|
||||||
|
execArgs: nil,
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "--rev", "1", "--", "echo", "Hello", "World"},
|
commandArgs: []string{"watch", "--rev", "100", "--prefix", "--prev-kv"},
|
||||||
envKey: "foo",
|
envKey: "foo",
|
||||||
envRange: "bar",
|
interactive: true,
|
||||||
interactive: true,
|
interactiveWatchPrefix: true,
|
||||||
watchArgs: []string{"foo", "bar"},
|
interactiveWatchRev: 100,
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
interactiveWatchPrevKey: true,
|
||||||
err: nil,
|
watchArgs: []string{"foo"},
|
||||||
|
execArgs: nil,
|
||||||
|
err: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "foo", "--rev", "1", "bar", "--", "echo", "Hello", "World"},
|
commandArgs: []string{"watch", "--rev", "1", "--prefix"},
|
||||||
interactive: true,
|
interactive: true,
|
||||||
watchArgs: []string{"foo", "bar"},
|
interactiveWatchPrefix: false,
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
interactiveWatchRev: 0,
|
||||||
err: nil,
|
interactiveWatchPrevKey: false,
|
||||||
|
watchArgs: nil,
|
||||||
|
execArgs: nil,
|
||||||
|
err: errBadArgsNum,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
osArgs: []string{"./bin/etcdctl", "watch", "-i"},
|
||||||
commandArgs: []string{"watch", "foo", "bar", "--rev", "1", "--", "echo", "Hello", "World"},
|
commandArgs: []string{"watch", "--", "echo", "Hello", "World"},
|
||||||
interactive: true,
|
envKey: "foo",
|
||||||
watchArgs: []string{"foo", "bar"},
|
interactive: true,
|
||||||
execArgs: []string{"echo", "Hello", "World"},
|
interactiveWatchPrefix: false,
|
||||||
err: nil,
|
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 {
|
for i, ts := range tt {
|
||||||
|
@ -355,5 +543,16 @@ func Test_parseWatchArgs(t *testing.T) {
|
||||||
if !reflect.DeepEqual(execArgs, ts.execArgs) {
|
if !reflect.DeepEqual(execArgs, ts.execArgs) {
|
||||||
t.Fatalf("#%d: execArgs expected %q, got %v", i, ts.execArgs, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
// Copyright 2018 The etcd Authors
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build cov
|
||||||
|
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCtlV3Watch(t *testing.T) { testCtl(t, watchTest) }
|
||||||
|
func TestCtlV3WatchNoTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configNoTLS)) }
|
||||||
|
func TestCtlV3WatchClientTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configClientTLS)) }
|
||||||
|
func TestCtlV3WatchPeerTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configPeerTLS)) }
|
||||||
|
func TestCtlV3WatchTimeout(t *testing.T) { testCtl(t, watchTest, withDialTimeout(0)) }
|
||||||
|
|
||||||
|
func TestCtlV3WatchInteractive(t *testing.T) {
|
||||||
|
testCtl(t, watchTest, withInteractive())
|
||||||
|
}
|
||||||
|
func TestCtlV3WatchInteractiveNoTLS(t *testing.T) {
|
||||||
|
testCtl(t, watchTest, withInteractive(), withCfg(configNoTLS))
|
||||||
|
}
|
||||||
|
func TestCtlV3WatchInteractiveClientTLS(t *testing.T) {
|
||||||
|
testCtl(t, watchTest, withInteractive(), withCfg(configClientTLS))
|
||||||
|
}
|
||||||
|
func TestCtlV3WatchInteractivePeerTLS(t *testing.T) {
|
||||||
|
testCtl(t, watchTest, withInteractive(), withCfg(configPeerTLS))
|
||||||
|
}
|
||||||
|
|
||||||
|
func watchTest(cx ctlCtx) {
|
||||||
|
tests := []struct {
|
||||||
|
puts []kv
|
||||||
|
envKey string
|
||||||
|
envRange string
|
||||||
|
args []string
|
||||||
|
|
||||||
|
wkv []kvExec
|
||||||
|
}{
|
||||||
|
{ // watch 1 key
|
||||||
|
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"}},
|
||||||
|
},
|
||||||
|
|
||||||
|
// coverage tests get extra arguments:
|
||||||
|
// ./bin/etcdctl_test -test.coverprofile=e2e.1525392462795198897.coverprofile -test.outputdir=../..
|
||||||
|
// do not test watch exec commands
|
||||||
|
|
||||||
|
{ // watch 3 keys by prefix
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
donec := make(chan struct{})
|
||||||
|
go func(i int, puts []kv) {
|
||||||
|
for j := range puts {
|
||||||
|
if err := ctlV3Put(cx, puts[j].key, puts[j].val, ""); err != nil {
|
||||||
|
cx.t.Fatalf("watchTest #%d-%d: ctlV3Put error (%v)", i, j, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
// Copyright 2018 The etcd Authors
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build !cov
|
||||||
|
|
||||||
|
package e2e
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCtlV3Watch(t *testing.T) { testCtl(t, watchTest) }
|
||||||
|
func TestCtlV3WatchNoTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configNoTLS)) }
|
||||||
|
func TestCtlV3WatchClientTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configClientTLS)) }
|
||||||
|
func TestCtlV3WatchPeerTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configPeerTLS)) }
|
||||||
|
func TestCtlV3WatchTimeout(t *testing.T) { testCtl(t, watchTest, withDialTimeout(0)) }
|
||||||
|
|
||||||
|
func TestCtlV3WatchInteractive(t *testing.T) {
|
||||||
|
testCtl(t, watchTest, withInteractive())
|
||||||
|
}
|
||||||
|
func TestCtlV3WatchInteractiveNoTLS(t *testing.T) {
|
||||||
|
testCtl(t, watchTest, withInteractive(), withCfg(configNoTLS))
|
||||||
|
}
|
||||||
|
func TestCtlV3WatchInteractiveClientTLS(t *testing.T) {
|
||||||
|
testCtl(t, watchTest, withInteractive(), withCfg(configClientTLS))
|
||||||
|
}
|
||||||
|
func TestCtlV3WatchInteractivePeerTLS(t *testing.T) {
|
||||||
|
testCtl(t, watchTest, withInteractive(), withCfg(configPeerTLS))
|
||||||
|
}
|
||||||
|
|
||||||
|
func watchTest(cx ctlCtx) {
|
||||||
|
tests := []struct {
|
||||||
|
puts []kv
|
||||||
|
envKey string
|
||||||
|
envRange string
|
||||||
|
args []string
|
||||||
|
|
||||||
|
wkv []kvExec
|
||||||
|
}{
|
||||||
|
{ // watch 1 key
|
||||||
|
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 ${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"
|
||||||
|
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!\""
|
||||||
|
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"
|
||||||
|
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"
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
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"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
donec := make(chan struct{})
|
||||||
|
go func(i int, puts []kv) {
|
||||||
|
for j := range puts {
|
||||||
|
if err := ctlV3Put(cx, puts[j].key, puts[j].val, ""); err != nil {
|
||||||
|
cx.t.Fatalf("watchTest #%d-%d: ctlV3Put error (%v)", i, j, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2016 The etcd Authors
|
// Copyright 2018 The etcd Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -14,167 +14,13 @@
|
||||||
|
|
||||||
package e2e
|
package e2e
|
||||||
|
|
||||||
import (
|
import "strings"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCtlV3Watch(t *testing.T) { testCtl(t, watchTest) }
|
|
||||||
func TestCtlV3WatchNoTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configNoTLS)) }
|
|
||||||
func TestCtlV3WatchClientTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configClientTLS)) }
|
|
||||||
func TestCtlV3WatchPeerTLS(t *testing.T) { testCtl(t, watchTest, withCfg(configPeerTLS)) }
|
|
||||||
func TestCtlV3WatchTimeout(t *testing.T) { testCtl(t, watchTest, withDialTimeout(0)) }
|
|
||||||
|
|
||||||
func TestCtlV3WatchInteractive(t *testing.T) {
|
|
||||||
testCtl(t, watchTest, withInteractive())
|
|
||||||
}
|
|
||||||
func TestCtlV3WatchInteractiveNoTLS(t *testing.T) {
|
|
||||||
testCtl(t, watchTest, withInteractive(), withCfg(configNoTLS))
|
|
||||||
}
|
|
||||||
func TestCtlV3WatchInteractiveClientTLS(t *testing.T) {
|
|
||||||
testCtl(t, watchTest, withInteractive(), withCfg(configClientTLS))
|
|
||||||
}
|
|
||||||
func TestCtlV3WatchInteractivePeerTLS(t *testing.T) {
|
|
||||||
testCtl(t, watchTest, withInteractive(), withCfg(configPeerTLS))
|
|
||||||
}
|
|
||||||
|
|
||||||
type kvExec struct {
|
type kvExec struct {
|
||||||
key, val string
|
key, val string
|
||||||
execOutput string
|
execOutput string
|
||||||
}
|
}
|
||||||
|
|
||||||
func watchTest(cx ctlCtx) {
|
|
||||||
tests := []struct {
|
|
||||||
puts []kv
|
|
||||||
envKey string
|
|
||||||
envRange string
|
|
||||||
args []string
|
|
||||||
|
|
||||||
wkv []kvExec
|
|
||||||
}{
|
|
||||||
{ // watch 1 key
|
|
||||||
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"
|
|
||||||
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"
|
|
||||||
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!\""
|
|
||||||
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"
|
|
||||||
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"
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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
|
|
||||||
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"}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
donec := make(chan struct{})
|
|
||||||
go func(i int, puts []kv) {
|
|
||||||
for j := range puts {
|
|
||||||
if err := ctlV3Put(cx, puts[j].key, puts[j].val, ""); err != nil {
|
|
||||||
cx.t.Fatalf("watchTest #%d-%d: ctlV3Put error (%v)", i, j, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupWatchArgs(cx ctlCtx, args []string) []string {
|
func setupWatchArgs(cx ctlCtx, args []string) []string {
|
||||||
cmdArgs := append(cx.PrefixArgs(), "watch")
|
cmdArgs := append(cx.PrefixArgs(), "watch")
|
||||||
if cx.interactive {
|
if cx.interactive {
|
||||||
|
|
Loading…
Reference in New Issue