diff --git a/pkg/cli/cli_debug_test.go b/pkg/cli/cli_debug_test.go index c04c0fbb8d47..857c1ab4b926 100644 --- a/pkg/cli/cli_debug_test.go +++ b/pkg/cli/cli_debug_test.go @@ -14,6 +14,15 @@ package cli +import ( + "path/filepath" + "strings" + "testing" + + "github.com/cockroachdb/cockroach/pkg/testutils" + "github.com/cockroachdb/cockroach/pkg/util/leaktest" +) + func Example_debug_decode_key_value() { cliTest{}.RunWithArgs([]string{"debug", "decode-value", "016b12bd8980c0b6c2e211ba5182000172647363", "884d186d03089b09120bbd8980c0b6c2e211ba51821a0bbd8980c0b9e7c5c610e99622060801100118012206080410041802220608021002180428053004"}) @@ -25,3 +34,39 @@ func Example_debug_decode_key_value() { // 0.987654321,0 /Local/Range/Table/53/1/-4560243296450227838/RangeDescriptor: [/Table/53/1/-4560243296450227838, /Table/53/1/-4559358311118345834) // Raw:r1179:/Table/53/1/-45{60243296450227838-59358311118345834} [(n1,s1):1, (n4,s4):2, (n2,s2):4, next=5, gen=4] } + +func TestDebugKeysHex(t *testing.T) { + defer leaktest.AfterTest(t)() + + baseDir, dirCleanupFn := testutils.TempDir(t) + defer dirCleanupFn() + + storePath := filepath.Join(baseDir, "store") + createStore(t, storePath) + + { + out, err := cliTest{}.RunWithCapture("debug keys " + storePath + + " --from hex:016b12bd898090d79e52e79b0144000174786e2d733fb094e9aa4d67974c71486b9823b900" + + " --to hex:016b12bd898090d79e52e79b0144000174786e2d733fb094e9aa4d67974c71486b9823b900") + if err != nil { + t.Fatal(err) + } + // Should just output the command invocation and no results. + if !strings.HasSuffix(strings.TrimSpace(out), "b900") { + t.Fatalf("%q", out) + } + } + + // Test invalid key, verify we get a good suggestion back. + out, err := cliTest{}.RunWithCapture("debug keys " + storePath + + " --to hex:01") + if err != nil { + t.Fatal(err) + } + expOut := `invalid argument "hex:01" for "--to" flag: perhaps this is just a hex-encoded key; ` + + `you need an encoded MVCCKey (i.e. with a timestamp component); here's one with a zero timestamp: ` + + `0100: invalid encoded mvcc key: 01` + if !strings.Contains(out, expOut) { + t.Fatalf("%q", out) + } +} diff --git a/pkg/cli/cliflags/flags.go b/pkg/cli/cliflags/flags.go index f7336be7eede..db1a13615e20 100644 --- a/pkg/cli/cliflags/flags.go +++ b/pkg/cli/cliflags/flags.go @@ -673,17 +673,17 @@ database, insecure, certs).`, From = FlagInfo{ Name: "from", Description: ` -Start key and format as [:]. Supported formats: raw, human, +Start key and format as [:]. Supported formats: raw, hex, human, rangeID. The raw format supports escaped text. For example, "raw:\x01k" is the -prefix for range local keys.`, +prefix for range local keys. The hex format takes an encoded MVCCKey.`, } To = FlagInfo{ Name: "to", Description: ` -Exclusive end key and format as [:]. Supported formats: raw, +Exclusive end key and format as [:]. Supported formats: raw, hex, human, rangeID. The raw format supports escaped text. For example, "raw:\x01k" -is the prefix for range local keys.`} +is the prefix for range local keys. The hex format takes an encoded MVCCKey.`} Values = FlagInfo{ Name: "values", diff --git a/pkg/cli/debug.go b/pkg/cli/debug.go index 56c30d037912..bc6a2839a307 100644 --- a/pkg/cli/debug.go +++ b/pkg/cli/debug.go @@ -19,7 +19,7 @@ import ( "bufio" "bytes" "context" - "encoding/hex" + gohex "encoding/hex" "fmt" "io" "math" @@ -337,7 +337,7 @@ Decode a hexadecimal-encoded key and pretty-print it. For example: Args: cobra.ArbitraryArgs, RunE: func(cmd *cobra.Command, args []string) error { for _, arg := range args { - b, err := hex.DecodeString(arg) + b, err := gohex.DecodeString(arg) if err != nil { return err } @@ -364,7 +364,7 @@ Decode and print a hexadecimal-encoded key-value pair. RunE: func(cmd *cobra.Command, args []string) error { var bs [][]byte for _, arg := range args { - b, err := hex.DecodeString(arg) + b, err := gohex.DecodeString(arg) if err != nil { return err } diff --git a/pkg/cli/flags_util.go b/pkg/cli/flags_util.go index 54bf825239dd..a5966e53e6f2 100644 --- a/pkg/cli/flags_util.go +++ b/pkg/cli/flags_util.go @@ -16,6 +16,7 @@ package cli import ( "context" + gohex "encoding/hex" "fmt" "math" "regexp" @@ -166,6 +167,19 @@ func (k *mvccKey) Set(value string) error { } switch typ { + case hex: + b, err := gohex.DecodeString(keyStr) + if err != nil { + return err + } + newK, err := engine.DecodeKey(b) + if err != nil { + encoded := gohex.EncodeToString(engine.EncodeKey(engine.MakeMVCCMetadataKey(roachpb.Key(b)))) + return errors.Wrapf(err, "perhaps this is just a hex-encoded key; you need an "+ + "encoded MVCCKey (i.e. with a timestamp component); here's one with a zero timestamp: %s", + encoded) + } + *k = mvccKey(newK) case raw: unquoted, err := unquoteArg(keyStr) if err != nil { @@ -208,6 +222,7 @@ const ( raw keyType = iota human rangeID + hex ) // _keyTypes stores the names of all the possible key types. diff --git a/pkg/cli/keytype_string.go b/pkg/cli/keytype_string.go index d6d3827c61b2..0c7e5a1d34da 100644 --- a/pkg/cli/keytype_string.go +++ b/pkg/cli/keytype_string.go @@ -4,9 +4,9 @@ package cli import "strconv" -const _keyType_name = "rawhumanrangeID" +const _keyType_name = "rawhumanrangeIDhex" -var _keyType_index = [...]uint8{0, 3, 8, 15} +var _keyType_index = [...]uint8{0, 3, 8, 15, 18} func (i keyType) String() string { if i < 0 || i >= keyType(len(_keyType_index)-1) { diff --git a/pkg/cmd/roachtest/cluster_test.go b/pkg/cmd/roachtest/cluster_test.go index cfed3f082ad7..a34b5bc76fa6 100644 --- a/pkg/cmd/roachtest/cluster_test.go +++ b/pkg/cmd/roachtest/cluster_test.go @@ -95,13 +95,52 @@ func TestClusterMonitor(t *testing.T) { return ctx.Err() }) - err := m.wait(`echo`, `1`) + err := m.wait(`sleep`, `100`) expectedErr := `worker-fail` if !testutils.IsError(err, expectedErr) { t.Errorf(`expected %s err got: %+v`, expectedErr, err) } }) + t.Run(`wait-fail`, func(t *testing.T) { + c := &cluster{t: t, l: logger} + m := newMonitor(context.Background(), c) + m.Go(func(ctx context.Context) error { + <-ctx.Done() + return ctx.Err() + }) + m.Go(func(ctx context.Context) error { + <-ctx.Done() + return ctx.Err() + }) + + // Returned error should be that from the wait command. + err := m.wait(`false`) + expectedErr := `exit status` + if !testutils.IsError(err, expectedErr) { + t.Errorf(`expected %s err got: %+v`, expectedErr, err) + } + }) + + t.Run(`wait-ok`, func(t *testing.T) { + c := &cluster{t: t, l: logger} + m := newMonitor(context.Background(), c) + m.Go(func(ctx context.Context) error { + <-ctx.Done() + return ctx.Err() + }) + m.Go(func(ctx context.Context) error { + <-ctx.Done() + return ctx.Err() + }) + + // If wait terminates, context gets canceled. + err := m.wait(`true`) + if err != context.Canceled { + t.Errorf(`expected context canceled, got: %+v`, err) + } + }) + // NB: the forker sleeps in these tests actually get leaked, so it's important to let // them finish pretty soon (think stress testing). As a matter of fact, `make test` waits // for these child goroutines to finish (so these tests take seconds). diff --git a/pkg/storage/closed_timestamp_test.go b/pkg/storage/closed_timestamp_test.go index 825f6c86cee0..dca2215f4f34 100644 --- a/pkg/storage/closed_timestamp_test.go +++ b/pkg/storage/closed_timestamp_test.go @@ -35,6 +35,7 @@ import ( func TestClosedTimestampCanServe(t *testing.T) { defer leaktest.AfterTest(t)() + t.Skip("https://github.com/cockroachdb/cockroach/issues/28607") ctx := context.Background() const numNodes = 3 diff --git a/pkg/storage/replica_sideload_test.go b/pkg/storage/replica_sideload_test.go index ee3e8e7dea1b..bd6f766bd20b 100644 --- a/pkg/storage/replica_sideload_test.go +++ b/pkg/storage/replica_sideload_test.go @@ -735,8 +735,6 @@ func TestRaftSSTableSideloadingSnapshot(t *testing.T) { ctx := context.Background() tc := testContext{} - stopper := stop.NewStopper() - defer stopper.Stop(ctx) cleanup, cache, eng := newRocksDB(t) tc.engine = eng @@ -744,6 +742,8 @@ func TestRaftSSTableSideloadingSnapshot(t *testing.T) { defer cache.Release() defer eng.Close() + stopper := stop.NewStopper() + defer stopper.Stop(ctx) tc.Start(t, stopper) var ba roachpb.BatchRequest