Merge pull request #723 from Elbehery/migrate_check_cobra

cmd: migrate `check` command to cobra style
pull/732/head
Benjamin Wang 2024-04-17 19:13:14 +01:00 committed by GitHub
commit df86a96bf7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 92 additions and 92 deletions

View File

@ -0,0 +1,58 @@
package main
import (
"fmt"
"github.com/spf13/cobra"
bolt "go.etcd.io/bbolt"
"go.etcd.io/bbolt/internal/guts_cli"
)
func newCheckCommand() *cobra.Command {
checkCmd := &cobra.Command{
Use: "check <bbolt-file>",
Short: "verify integrity of bbolt database data",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
return checkFunc(cmd, args[0])
},
}
return checkCmd
}
func checkFunc(cmd *cobra.Command, dbPath string) error {
if _, err := checkSourceDBPath(dbPath); err != nil {
return err
}
// Open database.
db, err := bolt.Open(dbPath, 0600, &bolt.Options{
ReadOnly: true,
PreLoadFreelist: true,
})
if err != nil {
return err
}
defer db.Close()
// Perform consistency check.
return db.View(func(tx *bolt.Tx) error {
var count int
for err := range tx.Check(bolt.WithKVStringer(CmdKvStringer())) {
fmt.Fprintln(cmd.OutOrStdout(), err)
count++
}
// Print summary of errors.
if count > 0 {
fmt.Fprintf(cmd.OutOrStdout(), "%d errors found\n", count)
return guts_cli.ErrCorrupt
}
// Notify user that database is valid.
fmt.Fprintln(cmd.OutOrStdout(), "OK")
return nil
})
}

View File

@ -0,0 +1,33 @@
package main_test
import (
"bytes"
"io"
"testing"
"github.com/stretchr/testify/require"
main "go.etcd.io/bbolt/cmd/bbolt"
"go.etcd.io/bbolt/internal/btesting"
)
func TestCheckCommand_Run(t *testing.T) {
db := btesting.MustCreateDB(t)
db.Close()
defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())
rootCmd := main.NewRootCommand()
// capture output for assertion
outputBuf := bytes.NewBufferString("")
rootCmd.SetOut(outputBuf)
rootCmd.SetArgs([]string{
"check", db.Path(),
})
err := rootCmd.Execute()
require.NoError(t, err)
output, err := io.ReadAll(outputBuf)
require.NoError(t, err)
require.Equalf(t, "OK\n", string(output), "unexpected stdout:\n\n%s", string(output))
}

View File

@ -20,6 +20,7 @@ func NewRootCommand() *cobra.Command {
newVersionCobraCommand(),
newSurgeryCobraCommand(),
newInspectCobraCommand(),
newCheckCommand(),
)
return rootCmd

View File

@ -123,8 +123,6 @@ func (m *Main) Run(args ...string) error {
return newBenchCommand(m).Run(args[1:]...)
case "buckets":
return newBucketsCommand(m).Run(args[1:]...)
case "check":
return newCheckCommand(m).Run(args[1:]...)
case "compact":
return newCompactCommand(m).Run(args[1:]...)
case "dump":
@ -180,82 +178,6 @@ Use "bbolt [command] -h" for more information about a command.
`, "\n")
}
// checkCommand represents the "check" command execution.
type checkCommand struct {
baseCommand
}
// newCheckCommand returns a checkCommand.
func newCheckCommand(m *Main) *checkCommand {
c := &checkCommand{}
c.baseCommand = m.baseCommand
return c
}
// Run executes the command.
func (cmd *checkCommand) Run(args ...string) error {
// Parse flags.
fs := flag.NewFlagSet("", flag.ContinueOnError)
help := fs.Bool("h", false, "")
if err := fs.Parse(args); err != nil {
return err
} else if *help {
fmt.Fprintln(cmd.Stderr, cmd.Usage())
return ErrUsage
}
// Require database path.
path := fs.Arg(0)
if path == "" {
return ErrPathRequired
} else if _, err := os.Stat(path); os.IsNotExist(err) {
return ErrFileNotFound
}
// Open database.
db, err := bolt.Open(path, 0600, &bolt.Options{
ReadOnly: true,
PreLoadFreelist: true,
})
if err != nil {
return err
}
defer db.Close()
// Perform consistency check.
return db.View(func(tx *bolt.Tx) error {
var count int
for err := range tx.Check(bolt.WithKVStringer(CmdKvStringer())) {
fmt.Fprintln(cmd.Stdout, err)
count++
}
// Print summary of errors.
if count > 0 {
fmt.Fprintf(cmd.Stdout, "%d errors found\n", count)
return guts_cli.ErrCorrupt
}
// Notify user that database is valid.
fmt.Fprintln(cmd.Stdout, "OK")
return nil
})
}
// Usage returns the help message.
func (cmd *checkCommand) Usage() string {
return strings.TrimLeft(`
usage: bolt check PATH
Check opens a database at PATH and runs an exhaustive check to verify that
all pages are accessible or are marked as freed. It also verifies that no
pages are double referenced.
Verification errors will stream out as they are found and the process will
return after all pages have been checked.
`, "\n")
}
// infoCommand represents the "info" command execution.
type infoCommand struct {
baseCommand

View File

@ -79,20 +79,6 @@ func TestStatsCommand_Run_EmptyDatabase(t *testing.T) {
}
}
func TestCheckCommand_Run(t *testing.T) {
db := btesting.MustCreateDB(t)
db.Close()
defer requireDBNoChange(t, dbData(t, db.Path()), db.Path())
m := NewMain()
err := m.Run("check", db.Path())
require.NoError(t, err)
if m.Stdout.String() != "OK\n" {
t.Fatalf("unexpected stdout:\n\n%s", m.Stdout.String())
}
}
func TestDumpCommand_Run(t *testing.T) {
db := btesting.MustCreateDBWithOption(t, &bolt.Options{PageSize: 4096})
db.Close()