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
Dane Strandboge 2021-06-08 15:19:21 -05:00 committed by GitHub
parent e59b8ea078
commit c665c749fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 52 deletions

View File

@ -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

View File

@ -23,5 +23,7 @@ func NewCommand(v *viper.Viper) (*cobra.Command, error) {
base.AddCommand(exportLp)
base.AddCommand(NewExportIndexCommand())
base.AddCommand(NewVerifySeriesfileCommand())
return base, nil
}

View File

@ -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

View File

@ -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))
}