diff --git a/e2e/etcdctlv3_test.go b/e2e/etcdctlv3_test.go index 05afa835c..cbb922c5d 100644 --- a/e2e/etcdctlv3_test.go +++ b/e2e/etcdctlv3_test.go @@ -42,6 +42,13 @@ func TestCtlV3GetPrefix(t *testing.T) { testCtl(t, getTest, withPrefix()) } func TestCtlV3GetPrefixLimit(t *testing.T) { testCtl(t, getTest, withPrefix(), withLimit(2)) } func TestCtlV3GetQuorum(t *testing.T) { testCtl(t, getTest, withQuorum()) } +func TestCtlV3Del(t *testing.T) { testCtl(t, delTest) } +func TestCtlV3DelNoTLS(t *testing.T) { testCtl(t, delTest, withCfg(configNoTLS)) } +func TestCtlV3DelClientTLS(t *testing.T) { testCtl(t, delTest, withCfg(configClientTLS)) } +func TestCtlV3DelPeerTLS(t *testing.T) { testCtl(t, delTest, withCfg(configPeerTLS)) } + +func TestCtlV3DelPrefix(t *testing.T) { testCtl(t, delTest, withPrefix()) } + 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)) } @@ -193,54 +200,23 @@ func putTest(cx ctlCtx) { if err := ctlV3Put(cx, key, value); err != nil { if cx.dialTimeout > 0 && isGRPCTimedout(err) { - cx.t.Fatalf("putTest error (%v)", err) + cx.t.Fatalf("putTest ctlV3Put error (%v)", err) } } if err := ctlV3Get(cx, key, kv{key, value}); err != nil { if cx.dialTimeout > 0 && isGRPCTimedout(err) { - cx.t.Fatalf("putTest error (%v)", err) + cx.t.Fatalf("putTest ctlV3Get error (%v)", err) } } } -func watchTest(cx ctlCtx) { - defer close(cx.errc) - - key, value := "foo", "bar" - - go func() { - if err := ctlV3Put(cx, key, value); err != nil { - cx.t.Fatalf("watchTest error (%v)", err) - } - }() - - if err := ctlV3Watch(cx, key, value); err != nil { - if cx.dialTimeout > 0 && isGRPCTimedout(err) { - cx.t.Fatalf("watchTest error (%v)", err) - } - } -} - -func versionTest(cx ctlCtx) { - defer close(cx.errc) - - if err := ctlV3Version(cx); err != nil { - cx.t.Fatalf("versionTest error (%v)", err) - } -} - func getTest(cx ctlCtx) { defer close(cx.errc) - var ( - kvs = []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}} - ) - + kvs := []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}} for i := range kvs { if err := ctlV3Put(cx, kvs[i].key, kvs[i].val); err != nil { - if cx.dialTimeout > 0 && isGRPCTimedout(err) { - cx.t.Fatalf("getTest error (%v)", err) - } + cx.t.Fatalf("getTest ctlV3Put error (%v)", err) } } @@ -256,19 +232,69 @@ func getTest(cx ctlCtx) { if err := ctlV3Get(cx, keyToGet, kvs...); err != nil { if cx.dialTimeout > 0 && isGRPCTimedout(err) { - cx.t.Fatalf("getTest error (%v)", err) + cx.t.Fatalf("getTest ctlV3Get error (%v)", err) } } } +func delTest(cx ctlCtx) { + defer close(cx.errc) + + kvs := []kv{{"key1", "val1"}, {"key2", "val2"}, {"key3", "val3"}} + for i := range kvs { + if err := ctlV3Put(cx, kvs[i].key, kvs[i].val); err != nil { + cx.t.Fatalf("delTest ctlV3Put error (%v)", err) + } + } + + var ( + keyToDel = "key" + deletedNum = 3 + ) + if !cx.prefix { + keyToDel = "key1" + deletedNum = 1 + } + + if err := ctlV3Del(cx, keyToDel, deletedNum); err != nil { + cx.t.Fatalf("delTest ctlV3Del error (%v)", err) + } +} + +func watchTest(cx ctlCtx) { + defer close(cx.errc) + + key, value := "foo", "bar" + + go func() { + if err := ctlV3Put(cx, key, value); err != nil { + cx.t.Fatalf("watchTest ctlV3Put error (%v)", err) + } + }() + + if err := ctlV3Watch(cx, key, value); err != nil { + if cx.dialTimeout > 0 && isGRPCTimedout(err) { + cx.t.Fatalf("watchTest ctlV3Watch error (%v)", err) + } + } +} + +func versionTest(cx ctlCtx) { + defer close(cx.errc) + + if err := ctlV3Version(cx); err != nil { + cx.t.Fatalf("versionTest ctlV3Version error (%v)", err) + } +} + func txnTestSuccess(cx ctlCtx) { defer close(cx.errc) if err := ctlV3Put(cx, "key1", "value1"); err != nil { - cx.t.Fatalf("txnTestSuccess error (%v)", err) + cx.t.Fatalf("txnTestSuccess ctlV3Put error (%v)", err) } if err := ctlV3Put(cx, "key2", "value2"); err != nil { - cx.t.Fatalf("txnTestSuccess error (%v)", err) + cx.t.Fatalf("txnTestSuccess ctlV3Put error (%v)", err) } rqs := txnRequests{ @@ -345,6 +371,14 @@ func ctlV3Get(cx ctlCtx, key string, kvs ...kv) error { return spawnWithExpects(cmdArgs, lines...) } +func ctlV3Del(cx ctlCtx, key string, num int) error { + cmdArgs := append(ctlV3PrefixArgs(cx.epc, cx.dialTimeout), "del", key) + if cx.prefix { + cmdArgs = append(cmdArgs, "--prefix") + } + return spawnWithExpects(cmdArgs, fmt.Sprintf("%d", num)) +} + func ctlV3Watch(cx ctlCtx, key, value string) error { cmdArgs := append(ctlV3PrefixArgs(cx.epc, cx.dialTimeout), "watch") if !cx.interactive { diff --git a/etcdctl/ctlv3/command/del_command.go b/etcdctl/ctlv3/command/del_command.go index 607666787..3582566ff 100644 --- a/etcdctl/ctlv3/command/del_command.go +++ b/etcdctl/ctlv3/command/del_command.go @@ -21,13 +21,20 @@ import ( "github.com/spf13/cobra" ) +var ( + delPrefix bool +) + // NewDelCommand returns the cobra command for "del". func NewDelCommand() *cobra.Command { - return &cobra.Command{ + cmd := &cobra.Command{ Use: "del [options] [range_end]", Short: "Removes the specified key or range of keys [key, range_end).", Run: delCommandFunc, } + + cmd.Flags().BoolVar(&delPrefix, "prefix", false, "delete keys with matching prefix") + return cmd } // delCommandFunc executes the "del" command. @@ -49,7 +56,15 @@ func getDelOp(cmd *cobra.Command, args []string) (string, []clientv3.OpOption) { opts := []clientv3.OpOption{} key := args[0] if len(args) > 1 { + if delPrefix { + ExitWithError(ExitBadArgs, fmt.Errorf("too many arguments, only accept one arguement when `--prefix` is set.")) + } opts = append(opts, clientv3.WithRange(args[1])) } + + if delPrefix { + opts = append(opts, clientv3.WithPrefix()) + } + return key, opts }