feat: Port influxd inspect verify-seriesfile (#21635)
* feat: Port influxd inspect verify-seriesfile * chore: clean up logic, use zaptest logger * chore: better error handling * chore: generic error return statement * chore: collapse nil check into if-block Co-authored-by: Daniel Moran <danxmoran@gmail.com> * chore: update changelog Co-authored-by: Daniel Moran <danxmoran@gmail.com>pull/21621/head^2
parent
e59b8ea078
commit
c665c749fe
|
@ -14,6 +14,7 @@ This release adds an embedded SQLite database for storing metadata required by t
|
|||
1. [21543](https://github.com/influxdata/influxdb/pull/21543): Updated `influxd` configuration flag `--store` to work with string values `disk` or `memory`. Memory continues to store metadata in-memory for testing; `disk` will persist metadata to disk via bolt and SQLite
|
||||
1. [21547](https://github.com/influxdata/influxdb/pull/21547): Allow hiding the tooltip independently of the static legend
|
||||
1. [21584](https://github.com/influxdata/influxdb/pull/21584): Added the `api/v2/backup/metadata` endpoint for backing up both KV and SQL metadata, and the `api/v2/restore/sql` for restoring SQL metadata.
|
||||
1. [21635](https://github.com/influxdata/influxdb/pull/21635): Port `influxd inspect verify-seriesfile` to 2.x
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
|
|
@ -23,5 +23,7 @@ func NewCommand(v *viper.Viper) (*cobra.Command, error) {
|
|||
base.AddCommand(exportLp)
|
||||
base.AddCommand(NewExportIndexCommand())
|
||||
|
||||
base.AddCommand(NewVerifySeriesfileCommand())
|
||||
|
||||
return base, nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package seriesfile
|
||||
package inspect
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
@ -9,10 +10,94 @@ import (
|
|||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/influxdata/influxdb/v2/logger"
|
||||
"github.com/influxdata/influxdb/v2/tsdb"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type args struct {
|
||||
dir string
|
||||
db string
|
||||
seriesFile string
|
||||
verbose bool
|
||||
concurrent int
|
||||
}
|
||||
|
||||
func NewVerifySeriesfileCommand() *cobra.Command {
|
||||
var arguments args
|
||||
cmd := &cobra.Command{
|
||||
Use: "verify-seriesfile",
|
||||
Short: "Verifies the integrity of series files.",
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cmd.SetOut(os.Stdout)
|
||||
|
||||
config := logger.NewConfig()
|
||||
config.Level = zapcore.WarnLevel
|
||||
if arguments.verbose {
|
||||
config.Level = zapcore.InfoLevel
|
||||
}
|
||||
log, err := config.New(os.Stderr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v := NewVerify()
|
||||
v.Logger = log
|
||||
v.Concurrent = arguments.concurrent
|
||||
|
||||
var db string
|
||||
if arguments.seriesFile != "" {
|
||||
db = arguments.seriesFile
|
||||
} else if arguments.db != "" {
|
||||
db = filepath.Join(arguments.dir, arguments.db, "_series")
|
||||
}
|
||||
if db != "" {
|
||||
_, err := v.VerifySeriesFile(db)
|
||||
return err
|
||||
}
|
||||
|
||||
dbs, err := ioutil.ReadDir(arguments.dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var hasError bool
|
||||
for _, db := range dbs {
|
||||
if !db.IsDir() {
|
||||
continue
|
||||
}
|
||||
filePath := filepath.Join(arguments.dir, db.Name(), "_series")
|
||||
if _, err := v.VerifySeriesFile(filePath); err != nil {
|
||||
v.Logger.Error("Failed to verify series file",
|
||||
zap.String("filename", filePath),
|
||||
zap.Error(err))
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
if hasError {
|
||||
return errors.New("some files failed verification, see logs for details")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVar(&arguments.dir, "dir", filepath.Join(os.Getenv("HOME"), ".influxdbv2", "engine", "data"),
|
||||
"Data Directory.")
|
||||
cmd.Flags().StringVar(&arguments.db, "db", "",
|
||||
"Only use this database inside of the data directory.")
|
||||
cmd.Flags().StringVar(&arguments.seriesFile, "series-file", "",
|
||||
"Path to a series file. This overrides --db and --dir.")
|
||||
cmd.Flags().BoolVar(&arguments.verbose, "v", false,
|
||||
"Verbose output.")
|
||||
cmd.Flags().IntVar(&arguments.concurrent, "c", runtime.GOMAXPROCS(0),
|
||||
"How many concurrent workers to run.")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// verifyResult contains the result of a Verify... call
|
||||
type verifyResult struct {
|
||||
valid bool
|
|
@ -1,4 +1,4 @@
|
|||
package seriesfile_test
|
||||
package inspect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -9,33 +9,40 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/influxdb/v2/cmd/influx_inspect/verify/seriesfile"
|
||||
"github.com/influxdata/influxdb/v2/models"
|
||||
"github.com/influxdata/influxdb/v2/tsdb"
|
||||
"go.uber.org/zap"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zaptest"
|
||||
)
|
||||
|
||||
func TestVerifies_BasicCobra(t *testing.T) {
|
||||
test := NewTest(t)
|
||||
defer os.RemoveAll(test.Path)
|
||||
|
||||
verify := NewVerifySeriesfileCommand()
|
||||
verify.SetArgs([]string{"--dir", test.Path})
|
||||
require.NoError(t, verify.Execute())
|
||||
}
|
||||
|
||||
func TestVerifies_Valid(t *testing.T) {
|
||||
test := NewTest(t)
|
||||
defer test.Close()
|
||||
defer os.RemoveAll(test.Path)
|
||||
|
||||
verify := NewVerify()
|
||||
verify.Logger = zaptest.NewLogger(t)
|
||||
|
||||
verify := seriesfile.NewVerify()
|
||||
if testing.Verbose() {
|
||||
verify.Logger, _ = zap.NewDevelopment()
|
||||
}
|
||||
passed, err := verify.VerifySeriesFile(test.Path)
|
||||
test.AssertNoError(err)
|
||||
test.Assert(passed)
|
||||
require.NoError(t, err)
|
||||
require.True(t, passed)
|
||||
}
|
||||
|
||||
func TestVerifies_Invalid(t *testing.T) {
|
||||
test := NewTest(t)
|
||||
defer test.Close()
|
||||
defer os.RemoveAll(test.Path)
|
||||
|
||||
require.NoError(t, filepath.Walk(test.Path, func(path string, info os.FileInfo, err error) error {
|
||||
require.NoError(t, err)
|
||||
|
||||
test.AssertNoError(filepath.Walk(test.Path, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
@ -44,25 +51,24 @@ func TestVerifies_Invalid(t *testing.T) {
|
|||
defer test.Restore(path)
|
||||
|
||||
fh, err := os.OpenFile(path, os.O_RDWR, 0)
|
||||
test.AssertNoError(err)
|
||||
require.NoError(t, err)
|
||||
defer fh.Close()
|
||||
|
||||
_, err = fh.WriteAt([]byte("BOGUS"), 0)
|
||||
test.AssertNoError(err)
|
||||
test.AssertNoError(fh.Close())
|
||||
_, err = fh.WriteAt([]byte("foobar"), 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fh.Close())
|
||||
|
||||
passed, err := seriesfile.NewVerify().VerifySeriesFile(test.Path)
|
||||
test.AssertNoError(err)
|
||||
test.Assert(!passed)
|
||||
verify := NewVerify()
|
||||
verify.Logger = zaptest.NewLogger(t)
|
||||
|
||||
passed, err := verify.VerifySeriesFile(test.Path)
|
||||
require.NoError(t, err)
|
||||
require.False(t, passed)
|
||||
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
//
|
||||
// helpers
|
||||
//
|
||||
|
||||
type Test struct {
|
||||
*testing.T
|
||||
Path string
|
||||
|
@ -72,9 +78,7 @@ func NewTest(t *testing.T) *Test {
|
|||
t.Helper()
|
||||
|
||||
dir, err := ioutil.TempDir("", "verify-seriesfile-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
// create a series file in the directory
|
||||
err = func() error {
|
||||
|
@ -135,39 +139,21 @@ func NewTest(t *testing.T) *Test {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *Test) Close() {
|
||||
os.RemoveAll(t.Path)
|
||||
}
|
||||
|
||||
func (t *Test) AssertNoError(err error) {
|
||||
t.Helper()
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Test) Assert(x bool) {
|
||||
t.Helper()
|
||||
if !x {
|
||||
t.Fatal("unexpected condition")
|
||||
}
|
||||
}
|
||||
|
||||
// Backup makes a copy of the path for a later Restore.
|
||||
func (t *Test) Backup(path string) {
|
||||
in, err := os.Open(path)
|
||||
t.AssertNoError(err)
|
||||
require.NoError(t.T, err)
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(path + ".backup")
|
||||
t.AssertNoError(err)
|
||||
require.NoError(t.T, err)
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
t.AssertNoError(err)
|
||||
require.NoError(t.T, err)
|
||||
}
|
||||
|
||||
// Restore restores the file at the path to the time when Backup was called last.
|
||||
func (t *Test) Restore(path string) {
|
||||
t.AssertNoError(os.Rename(path+".backup", path))
|
||||
require.NoError(t.T, os.Rename(path+".backup", path))
|
||||
}
|
Loading…
Reference in New Issue