etcdctl: add support for filtering by {min,max} x {create,mod} x {revision}

Signed-off-by: Cristian Ferretti <jcferretti2020@gmail.com>
This commit is contained in:
Cristian Ferretti 2024-07-02 12:48:00 -04:00
parent a897676d54
commit 07b35dd7e3
3 changed files with 88 additions and 14 deletions

View File

@ -125,6 +125,14 @@ RPC: Range
- keys-only -- Get only the keys - keys-only -- Get only the keys
- max-create-revision -- restrict results to kvs with create revision lower or equal than the supplied revision
- min-create-revision -- restrict results to kvs with create revision greater or equal than the supplied revision
- max-mod-revision -- restrict results to kvs with modified revision lower or equal than the supplied revision
- min-mod-revision -- restrict results to kvs with modified revision greater or equal than the supplied revision
#### Output #### Output
Prints the data in format below, Prints the data in format below,
``` ```

View File

@ -25,16 +25,20 @@ import (
) )
var ( var (
getConsistency string getConsistency string
getLimit int64 getLimit int64
getSortOrder string getSortOrder string
getSortTarget string getSortTarget string
getPrefix bool getPrefix bool
getFromKey bool getFromKey bool
getRev int64 getRev int64
getKeysOnly bool getKeysOnly bool
getCountOnly bool getCountOnly bool
printValueOnly bool printValueOnly bool
getMinCreateRev int64
getMaxCreateRev int64
getMinModRev int64
getMaxModRev int64
) )
// NewGetCommand returns the cobra command for "get". // NewGetCommand returns the cobra command for "get".
@ -55,6 +59,10 @@ func NewGetCommand() *cobra.Command {
cmd.Flags().BoolVar(&getKeysOnly, "keys-only", false, "Get only the keys") cmd.Flags().BoolVar(&getKeysOnly, "keys-only", false, "Get only the keys")
cmd.Flags().BoolVar(&getCountOnly, "count-only", false, "Get only the count") cmd.Flags().BoolVar(&getCountOnly, "count-only", false, "Get only the count")
cmd.Flags().BoolVar(&printValueOnly, "print-value-only", false, `Only write values when using the "simple" output format`) cmd.Flags().BoolVar(&printValueOnly, "print-value-only", false, `Only write values when using the "simple" output format`)
cmd.Flags().Int64Var(&getMinCreateRev, "min-create-rev", 0, "Minimum create revision")
cmd.Flags().Int64Var(&getMaxCreateRev, "max-create-rev", 0, "Maximum create revision")
cmd.Flags().Int64Var(&getMinModRev, "min-mod-rev", 0, "Minimum modification revision")
cmd.Flags().Int64Var(&getMaxModRev, "max-mod-rev", 0, "Maximum modification revision")
cmd.RegisterFlagCompletionFunc("consistency", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { cmd.RegisterFlagCompletionFunc("consistency", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"l", "s"}, cobra.ShellCompDirectiveDefault return []string{"l", "s"}, cobra.ShellCompDirectiveDefault
@ -184,5 +192,29 @@ func getGetOp(args []string) (string, []clientv3.OpOption) {
opts = append(opts, clientv3.WithCountOnly()) opts = append(opts, clientv3.WithCountOnly())
} }
if getMinCreateRev > 0 {
opts = append(opts, clientv3.WithMinCreateRev(getMinCreateRev))
}
if getMaxCreateRev > 0 {
if getMinCreateRev > getMaxCreateRev {
cobrautl.ExitWithError(cobrautl.ExitBadFeature,
fmt.Errorf("getMinCreateRev(=%v) > getMaxCreateRev(=%v)", getMinCreateRev, getMaxCreateRev))
}
opts = append(opts, clientv3.WithMaxCreateRev(getMaxCreateRev))
}
if getMinModRev > 0 {
opts = append(opts, clientv3.WithMinModRev(getMinModRev))
}
if getMaxModRev > 0 {
if getMinModRev > getMaxModRev {
cobrautl.ExitWithError(cobrautl.ExitBadFeature,
fmt.Errorf("getMinModRev(=%v) > getMaxModRev(=%v)", getMinModRev, getMaxModRev))
}
opts = append(opts, clientv3.WithMaxModRev(getMaxModRev))
}
return key, opts return key, opts
} }

View File

@ -37,10 +37,11 @@ func TestCtlV3PutIgnoreLease(t *testing.T) { testCtl(t, putTestIgnoreLease) }
func TestCtlV3GetTimeout(t *testing.T) { testCtl(t, getTest, withDefaultDialTimeout()) } func TestCtlV3GetTimeout(t *testing.T) { testCtl(t, getTest, withDefaultDialTimeout()) }
func TestCtlV3GetFormat(t *testing.T) { testCtl(t, getFormatTest) } func TestCtlV3GetFormat(t *testing.T) { testCtl(t, getFormatTest) }
func TestCtlV3GetRev(t *testing.T) { testCtl(t, getRevTest) } func TestCtlV3GetRev(t *testing.T) { testCtl(t, getRevTest) }
func TestCtlV3GetKeysOnly(t *testing.T) { testCtl(t, getKeysOnlyTest) } func TestCtlV3GetMinMaxCreateModRev(t *testing.T) { testCtl(t, getMinMaxCreateModRevTest) }
func TestCtlV3GetCountOnly(t *testing.T) { testCtl(t, getCountOnlyTest) } func TestCtlV3GetKeysOnly(t *testing.T) { testCtl(t, getKeysOnlyTest) }
func TestCtlV3GetCountOnly(t *testing.T) { testCtl(t, getCountOnlyTest) }
func TestCtlV3DelTimeout(t *testing.T) { testCtl(t, delTest, withDefaultDialTimeout()) } func TestCtlV3DelTimeout(t *testing.T) { testCtl(t, delTest, withDefaultDialTimeout()) }
@ -216,6 +217,39 @@ func getRevTest(cx ctlCtx) {
} }
} }
func getMinMaxCreateModRevTest(cx ctlCtx) {
var (
kvs = []kv{ // revision: store | key create | key modify
{"key1", "val1"}, // 2 2 2
{"key2", "val2"}, // 3 3 3
{"key1", "val3"}, // 4 2 4
{"key4", "val4"}, // 5 5 5
}
)
for i := range kvs {
if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil {
cx.t.Fatalf("getRevTest #%d: ctlV3Put error (%v)", i, err)
}
}
tests := []struct {
args []string
wkv []kv
}{
{[]string{"key", "--prefix", "--max-create-rev", "3"}, []kv{kvs[1], kvs[2]}},
{[]string{"key", "--prefix", "--min-create-rev", "3"}, []kv{kvs[1], kvs[3]}},
{[]string{"key", "--prefix", "--max-mod-rev", "3"}, []kv{kvs[1]}},
{[]string{"key", "--prefix", "--min-mod-rev", "4"}, kvs[2:]},
}
for i, tt := range tests {
if err := ctlV3Get(cx, tt.args, tt.wkv...); err != nil {
cx.t.Errorf("getMinModRevTest #%d: ctlV3Get error (%v)", i, err)
}
}
}
func getKeysOnlyTest(cx ctlCtx) { func getKeysOnlyTest(cx ctlCtx) {
if err := ctlV3Put(cx, "key", "val", ""); err != nil { if err := ctlV3Put(cx, "key", "val", ""); err != nil {
cx.t.Fatal(err) cx.t.Fatal(err)