Skip to content

Commit

Permalink
Merge pull request #723 from Elbehery/migrate_check_cobra
Browse files Browse the repository at this point in the history
cmd: migrate `check` command to cobra style
  • Loading branch information
ahrtr authored Apr 17, 2024
2 parents cc22218 + e3afa40 commit df86a96
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 92 deletions.
58 changes: 58 additions & 0 deletions cmd/bbolt/command_check.go
Original file line number Diff line number Diff line change
@@ -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
})
}
33 changes: 33 additions & 0 deletions cmd/bbolt/command_check_test.go
Original file line number Diff line number Diff line change
@@ -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))
}
1 change: 1 addition & 0 deletions cmd/bbolt/command_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func NewRootCommand() *cobra.Command {
newVersionCobraCommand(),
newSurgeryCobraCommand(),
newInspectCobraCommand(),
newCheckCommand(),
)

return rootCmd
Expand Down
78 changes: 0 additions & 78 deletions cmd/bbolt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down Expand Up @@ -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
Expand Down
14 changes: 0 additions & 14 deletions cmd/bbolt/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit df86a96

Please sign in to comment.