diff --git a/cmd/bbolt/command_buckets.go b/cmd/bbolt/command_buckets.go new file mode 100644 index 000000000..8b522494c --- /dev/null +++ b/cmd/bbolt/command_buckets.go @@ -0,0 +1,51 @@ +package main + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + bolt "go.etcd.io/bbolt" +) + +// newBucketsCommand creates a new command that prints a list of buckets in the given Bolt database. +// +// The path to a Bolt database must be specified as an argument. +func newBucketsCommand() *cobra.Command { + var bucketsCmd = &cobra.Command{ + Use: "buckets ", + Short: "Print a list of buckets", + Long: "Print a list of buckets in the given Bolt database\nThe path to a Bolt database must be specified as an argument", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return printBucketsList(cmd, args[0]) + }, + } + return bucketsCmd +} + +// printBucketsList prints a list of buckets in the given Bolt database. +func printBucketsList(cmd *cobra.Command, path string) error { + // Required database path. + if path == "" { + return ErrPathRequired + // Verify if the specified database file exists. + } else if _, err := os.Stat(path); os.IsNotExist(err) { + return ErrFileNotFound + } + + // Open database. + db, err := bolt.Open(path, 0600, &bolt.Options{ReadOnly: true}) + if err != nil { + return err + } + defer db.Close() + + // Print the list of buckets in the database. + return db.View(func(tx *bolt.Tx) error { + return tx.ForEach(func(name []byte, _ *bolt.Bucket) error { + fmt.Fprintln(cmd.OutOrStdout(), string(name)) + return nil + }) + }) +} diff --git a/cmd/bbolt/command_buckets_test.go b/cmd/bbolt/command_buckets_test.go new file mode 100644 index 000000000..38d66f0cd --- /dev/null +++ b/cmd/bbolt/command_buckets_test.go @@ -0,0 +1,53 @@ +package main_test + +import ( + "bytes" + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + + bolt "go.etcd.io/bbolt" + main "go.etcd.io/bbolt/cmd/bbolt" + "go.etcd.io/bbolt/internal/btesting" +) + +// Ensure the "buckets" command can print a list of buckets. +func TestBuckets(t *testing.T) { + // Create a test database and populate it with sample buckets. + t.Log("Creating sample DB") + db := btesting.MustCreateDB(t) + t.Log("Creating sample Buckets") + if err := db.Update(func(tx *bolt.Tx) error { + for _, name := range []string{"foo", "bar", "baz"} { + _, err := tx.CreateBucket([]byte(name)) + if err != nil { + return err + } + } + return nil + }); err != nil { + t.Fatal(err) + } + db.Close() + defer requireDBNoChange(t, dbData(t, db.Path()), db.Path()) + + // setup the bucketscommand. + t.Log("Running buckets command") + rootCmd := main.NewRootCommand() + _, actualOutput, err := executeCommand(rootCmd, "buckets", db.Path()) //rootCmd, "buckets", db.Path()) + require.NoError(t, err) + t.Log("Verify result") + expected := "bar\nbaz\nfoo\n" + require.EqualValues(t, expected, actualOutput) +} + +// executeCommand runs the given command and returns the output and error. +func executeCommand(rootCmd *cobra.Command, args ...string) (*cobra.Command, string, error) { + outputBuf := bytes.NewBufferString("") + rootCmd.SetOut(outputBuf) + rootCmd.SetErr(outputBuf) + rootCmd.SetArgs(args) + c, err := rootCmd.ExecuteC() + return c, outputBuf.String(), err +} diff --git a/cmd/bbolt/command_root.go b/cmd/bbolt/command_root.go index 0336ea36c..a278458b7 100644 --- a/cmd/bbolt/command_root.go +++ b/cmd/bbolt/command_root.go @@ -21,6 +21,7 @@ func NewRootCommand() *cobra.Command { newSurgeryCommand(), newInspectCommand(), newCheckCommand(), + newBucketsCommand(), ) return rootCmd diff --git a/cmd/bbolt/main.go b/cmd/bbolt/main.go index a9256a699..b934b9900 100644 --- a/cmd/bbolt/main.go +++ b/cmd/bbolt/main.go @@ -122,8 +122,6 @@ func (m *Main) Run(args ...string) error { return ErrUsage case "bench": return newBenchCommand(m).Run(args[1:]...) - case "buckets": - return newBucketsCommand(m).Run(args[1:]...) case "compact": return newCompactCommand(m).Run(args[1:]...) case "dump": @@ -763,63 +761,6 @@ experience corruption, please submit a ticket to the etcd-io/bbolt project page: `, "\n") } -// bucketsCommand represents the "buckets" command execution. -type bucketsCommand struct { - baseCommand -} - -// newBucketsCommand returns a bucketsCommand. -func newBucketsCommand(m *Main) *bucketsCommand { - c := &bucketsCommand{} - c.baseCommand = m.baseCommand - return c -} - -// Run executes the command. -func (cmd *bucketsCommand) 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}) - if err != nil { - return err - } - defer db.Close() - - // Print buckets. - return db.View(func(tx *bolt.Tx) error { - return tx.ForEach(func(name []byte, _ *bolt.Bucket) error { - fmt.Fprintln(cmd.Stdout, string(name)) - return nil - }) - }) -} - -// Usage returns the help message. -func (cmd *bucketsCommand) Usage() string { - return strings.TrimLeft(` -usage: bolt buckets PATH - -Print a list of buckets. -`, "\n") -} - // keysCommand represents the "keys" command execution. type keysCommand struct { baseCommand diff --git a/cmd/bbolt/main_test.go b/cmd/bbolt/main_test.go index 727b38f55..ef2b6f78e 100644 --- a/cmd/bbolt/main_test.go +++ b/cmd/bbolt/main_test.go @@ -274,36 +274,6 @@ func TestStatsCommand_Run(t *testing.T) { } } -// Ensure the "buckets" command can print a list of buckets. -func TestBucketsCommand_Run(t *testing.T) { - db := btesting.MustCreateDB(t) - - if err := db.Update(func(tx *bolt.Tx) error { - for _, name := range []string{"foo", "bar", "baz"} { - _, err := tx.CreateBucket([]byte(name)) - if err != nil { - return err - } - } - return nil - }); err != nil { - t.Fatal(err) - } - db.Close() - - defer requireDBNoChange(t, dbData(t, db.Path()), db.Path()) - - expected := "bar\nbaz\nfoo\n" - - // Run the command. - m := NewMain() - if err := m.Run("buckets", db.Path()); err != nil { - t.Fatal(err) - } else if actual := m.Stdout.String(); actual != expected { - t.Fatalf("unexpected stdout:\n\n%s", actual) - } -} - // Ensure the "keys" command can print a list of keys for a bucket. func TestKeysCommand_Run(t *testing.T) { testCases := []struct {