Skip to content

Commit

Permalink
feat: Port influxd inspect verify-seriesfile (#21635)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
serenibyss and danxmoran authored Jun 8, 2021
1 parent e59b8ea commit c665c74
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 52 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions cmd/influxd/inspect/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ func NewCommand(v *viper.Viper) (*cobra.Command, error) {
base.AddCommand(exportLp)
base.AddCommand(NewExportIndexCommand())

base.AddCommand(NewVerifySeriesfileCommand())

return base, nil
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package seriesfile
package inspect

import (
"errors"
"fmt"
"io/ioutil"
"os"
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package seriesfile_test
package inspect

import (
"fmt"
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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))
}

0 comments on commit c665c74

Please sign in to comment.