diff --git a/cmd/promtool/main.go b/cmd/promtool/main.go
index e713a177f..d1b6c0fcd 100644
--- a/cmd/promtool/main.go
+++ b/cmd/promtool/main.go
@@ -236,14 +236,14 @@ func main() {
tsdbDumpCmd := tsdbCmd.Command("dump", "Dump samples from a TSDB.")
dumpPath := tsdbDumpCmd.Arg("db path", "Database path (default is "+defaultDBPath+").").Default(defaultDBPath).String()
- dumpSandboxDirRoot := tsdbDumpCmd.Flag("sandbox-dir-root", "Root directory where a sandbox directory would be created in case WAL replay generates chunks. The sandbox directory is cleaned up at the end.").Default(defaultDBPath).String()
+ dumpSandboxDirRoot := tsdbDumpCmd.Flag("sandbox-dir-root", "Root directory where a sandbox directory will be created, this sandbox is used in case WAL replay generates chunks (default is the database path). The sandbox is cleaned up at the end.").String()
dumpMinTime := tsdbDumpCmd.Flag("min-time", "Minimum timestamp to dump.").Default(strconv.FormatInt(math.MinInt64, 10)).Int64()
dumpMaxTime := tsdbDumpCmd.Flag("max-time", "Maximum timestamp to dump.").Default(strconv.FormatInt(math.MaxInt64, 10)).Int64()
dumpMatch := tsdbDumpCmd.Flag("match", "Series selector. Can be specified multiple times.").Default("{__name__=~'(?s:.*)'}").Strings()
tsdbDumpOpenMetricsCmd := tsdbCmd.Command("dump-openmetrics", "[Experimental] Dump samples from a TSDB into OpenMetrics text format, excluding native histograms and staleness markers, which are not representable in OpenMetrics.")
dumpOpenMetricsPath := tsdbDumpOpenMetricsCmd.Arg("db path", "Database path (default is "+defaultDBPath+").").Default(defaultDBPath).String()
- dumpOpenMetricsSandboxDirRoot := tsdbDumpOpenMetricsCmd.Flag("sandbox-dir-root", "Root directory where a sandbox directory would be created in case WAL replay generates chunks. The sandbox directory is cleaned up at the end.").Default(defaultDBPath).String()
+ dumpOpenMetricsSandboxDirRoot := tsdbDumpOpenMetricsCmd.Flag("sandbox-dir-root", "Root directory where a sandbox directory will be created, this sandbox is used in case WAL replay generates chunks (default is the database path). The sandbox is cleaned up at the end.").String()
dumpOpenMetricsMinTime := tsdbDumpOpenMetricsCmd.Flag("min-time", "Minimum timestamp to dump.").Default(strconv.FormatInt(math.MinInt64, 10)).Int64()
dumpOpenMetricsMaxTime := tsdbDumpOpenMetricsCmd.Flag("max-time", "Maximum timestamp to dump.").Default(strconv.FormatInt(math.MaxInt64, 10)).Int64()
dumpOpenMetricsMatch := tsdbDumpOpenMetricsCmd.Flag("match", "Series selector. Can be specified multiple times.").Default("{__name__=~'(?s:.*)'}").Strings()
diff --git a/cmd/promtool/main_test.go b/cmd/promtool/main_test.go
index 78500fe93..2b086851f 100644
--- a/cmd/promtool/main_test.go
+++ b/cmd/promtool/main_test.go
@@ -35,6 +35,7 @@ import (
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/rulefmt"
+ "github.com/prometheus/prometheus/promql/promqltest"
)
var promtoolPath = os.Args[0]
@@ -549,3 +550,46 @@ func TestCheckRulesWithRuleFiles(t *testing.T) {
require.Equal(t, lintErrExitCode, exitCode, "")
})
}
+
+func TestTSDBDumpCommand(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping test in short mode.")
+ }
+
+ storage := promqltest.LoadedStorage(t, `
+ load 1m
+ metric{foo="bar"} 1 2 3
+ `)
+ t.Cleanup(func() { storage.Close() })
+
+ for _, c := range []struct {
+ name string
+ subCmd string
+ sandboxDirRoot string
+ }{
+ {
+ name: "dump",
+ subCmd: "dump",
+ },
+ {
+ name: "dump with sandbox dir root",
+ subCmd: "dump",
+ sandboxDirRoot: t.TempDir(),
+ },
+ {
+ name: "dump-openmetrics",
+ subCmd: "dump-openmetrics",
+ },
+ {
+ name: "dump-openmetrics with sandbox dir root",
+ subCmd: "dump-openmetrics",
+ sandboxDirRoot: t.TempDir(),
+ },
+ } {
+ t.Run(c.name, func(t *testing.T) {
+ args := []string{"-test.main", "tsdb", c.subCmd, storage.Dir()}
+ cmd := exec.Command(promtoolPath, args...)
+ require.NoError(t, cmd.Run())
+ })
+ }
+}
diff --git a/cmd/promtool/tsdb_test.go b/cmd/promtool/tsdb_test.go
index ffc5467b4..90192e31a 100644
--- a/cmd/promtool/tsdb_test.go
+++ b/cmd/promtool/tsdb_test.go
@@ -55,7 +55,7 @@ func TestGenerateBucket(t *testing.T) {
}
// getDumpedSamples dumps samples and returns them.
-func getDumpedSamples(t *testing.T, path string, mint, maxt int64, match []string, formatter SeriesSetFormatter) string {
+func getDumpedSamples(t *testing.T, databasePath, sandboxDirRoot string, mint, maxt int64, match []string, formatter SeriesSetFormatter) string {
t.Helper()
oldStdout := os.Stdout
@@ -64,8 +64,8 @@ func getDumpedSamples(t *testing.T, path string, mint, maxt int64, match []strin
err := dumpSamples(
context.Background(),
- path,
- t.TempDir(),
+ databasePath,
+ sandboxDirRoot,
mint,
maxt,
match,
@@ -96,13 +96,15 @@ func TestTSDBDump(t *testing.T) {
heavy_metric{foo="bar"} 5 4 3 2 1
heavy_metric{foo="foo"} 5 4 3 2 1
`)
+ t.Cleanup(func() { storage.Close() })
tests := []struct {
- name string
- mint int64
- maxt int64
- match []string
- expectedDump string
+ name string
+ mint int64
+ maxt int64
+ sandboxDirRoot string
+ match []string
+ expectedDump string
}{
{
name: "default match",
@@ -111,6 +113,14 @@ func TestTSDBDump(t *testing.T) {
match: []string{"{__name__=~'(?s:.*)'}"},
expectedDump: "testdata/dump-test-1.prom",
},
+ {
+ name: "default match with sandbox dir root set",
+ mint: math.MinInt64,
+ maxt: math.MaxInt64,
+ sandboxDirRoot: t.TempDir(),
+ match: []string{"{__name__=~'(?s:.*)'}"},
+ expectedDump: "testdata/dump-test-1.prom",
+ },
{
name: "same matcher twice",
mint: math.MinInt64,
@@ -149,7 +159,7 @@ func TestTSDBDump(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- dumpedMetrics := getDumpedSamples(t, storage.Dir(), tt.mint, tt.maxt, tt.match, formatSeriesSet)
+ dumpedMetrics := getDumpedSamples(t, storage.Dir(), tt.sandboxDirRoot, tt.mint, tt.maxt, tt.match, formatSeriesSet)
expectedMetrics, err := os.ReadFile(tt.expectedDump)
require.NoError(t, err)
expectedMetrics = normalizeNewLine(expectedMetrics)
@@ -171,12 +181,29 @@ func TestTSDBDumpOpenMetrics(t *testing.T) {
my_counter{foo="bar", baz="abc"} 1 2 3 4 5
my_gauge{bar="foo", abc="baz"} 9 8 0 4 7
`)
+ t.Cleanup(func() { storage.Close() })
- expectedMetrics, err := os.ReadFile("testdata/dump-openmetrics-test.prom")
- require.NoError(t, err)
- expectedMetrics = normalizeNewLine(expectedMetrics)
- dumpedMetrics := getDumpedSamples(t, storage.Dir(), math.MinInt64, math.MaxInt64, []string{"{__name__=~'(?s:.*)'}"}, formatSeriesSetOpenMetrics)
- require.Equal(t, sortLines(string(expectedMetrics)), sortLines(dumpedMetrics))
+ tests := []struct {
+ name string
+ sandboxDirRoot string
+ }{
+ {
+ name: "default match",
+ },
+ {
+ name: "default match with sandbox dir root set",
+ sandboxDirRoot: t.TempDir(),
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ expectedMetrics, err := os.ReadFile("testdata/dump-openmetrics-test.prom")
+ require.NoError(t, err)
+ expectedMetrics = normalizeNewLine(expectedMetrics)
+ dumpedMetrics := getDumpedSamples(t, storage.Dir(), tt.sandboxDirRoot, math.MinInt64, math.MaxInt64, []string{"{__name__=~'(?s:.*)'}"}, formatSeriesSetOpenMetrics)
+ require.Equal(t, sortLines(string(expectedMetrics)), sortLines(dumpedMetrics))
+ })
+ }
}
func TestTSDBDumpOpenMetricsRoundTrip(t *testing.T) {
@@ -195,7 +222,7 @@ func TestTSDBDumpOpenMetricsRoundTrip(t *testing.T) {
})
// Dump the blocks into OM format
- dumpedMetrics := getDumpedSamples(t, dbDir, math.MinInt64, math.MaxInt64, []string{"{__name__=~'(?s:.*)'}"}, formatSeriesSetOpenMetrics)
+ dumpedMetrics := getDumpedSamples(t, dbDir, "", math.MinInt64, math.MaxInt64, []string{"{__name__=~'(?s:.*)'}"}, formatSeriesSetOpenMetrics)
// Should get back the initial metrics.
require.Equal(t, string(initialMetrics), dumpedMetrics)
diff --git a/docs/command-line/promtool.md b/docs/command-line/promtool.md
index 6d74200e6..e48cede79 100644
--- a/docs/command-line/promtool.md
+++ b/docs/command-line/promtool.md
@@ -575,7 +575,7 @@ Dump samples from a TSDB.
| Flag | Description | Default |
| --- | --- | --- |
-| --sandbox-dir-root
| Root directory where a sandbox directory would be created in case WAL replay generates chunks. The sandbox directory is cleaned up at the end. | `data/` |
+| --sandbox-dir-root
| Root directory where a sandbox directory will be created, this sandbox is used in case WAL replay generates chunks (default is the database path). The sandbox is cleaned up at the end. | |
| --min-time
| Minimum timestamp to dump. | `-9223372036854775808` |
| --max-time
| Maximum timestamp to dump. | `9223372036854775807` |
| --match
... | Series selector. Can be specified multiple times. | `{__name__=~'(?s:.*)'}` |
@@ -602,7 +602,7 @@ Dump samples from a TSDB.
| Flag | Description | Default |
| --- | --- | --- |
-| --sandbox-dir-root
| Root directory where a sandbox directory would be created in case WAL replay generates chunks. The sandbox directory is cleaned up at the end. | `data/` |
+| --sandbox-dir-root
| Root directory where a sandbox directory will be created, this sandbox is used in case WAL replay generates chunks (default is the database path). The sandbox is cleaned up at the end. | |
| --min-time
| Minimum timestamp to dump. | `-9223372036854775808` |
| --max-time
| Maximum timestamp to dump. | `9223372036854775807` |
| --match
... | Series selector. Can be specified multiple times. | `{__name__=~'(?s:.*)'}` |