diff --git a/cmd.go b/cmd.go deleted file mode 100644 index 73f54d17f..000000000 --- a/cmd.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021 Molecula Corp. All rights reserved. -package pilosa - -import ( - "io" - - "github.com/molecula/featurebase/v3/logger" -) - -// CmdIO holds standard unix inputs and outputs. -type CmdIO struct { - Stdin io.Reader - Stdout io.Writer - Stderr io.Writer - logger logger.Logger -} - -// NewCmdIO returns a new instance of CmdIO with inputs and outputs set to the -// arguments. -func NewCmdIO(stdin io.Reader, stdout, stderr io.Writer) *CmdIO { - return &CmdIO{ - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - logger: logger.NewStandardLogger(stderr), - } -} - -func (c *CmdIO) Logger() logger.Logger { - return c.logger -} diff --git a/cmd/auth_token.go b/cmd/auth_token.go index 5b4ac0eb2..b87570d0f 100644 --- a/cmd/auth_token.go +++ b/cmd/auth_token.go @@ -2,14 +2,13 @@ package cmd import ( - "io" - "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/spf13/cobra" ) -func newAuthTokenCommand(stdin io.Reader, stdout io.Writer, stderr io.Writer) *cobra.Command { - cmd := ctl.NewAuthTokenCommand(stdin, stdout, stderr) +func newAuthTokenCommand(logdest logger.Logger) *cobra.Command { + cmd := ctl.NewAuthTokenCommand(logdest) ccmd := &cobra.Command{ Use: "auth-token", Short: "Get an auth-token", diff --git a/cmd/backup.go b/cmd/backup.go index 275b62d2d..bb9daa1e4 100644 --- a/cmd/backup.go +++ b/cmd/backup.go @@ -2,14 +2,13 @@ package cmd import ( - "io" - "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/spf13/cobra" ) -func newBackupCommand(stdin io.Reader, stdout io.Writer, stderr io.Writer) *cobra.Command { - cmd := ctl.NewBackupCommand(stdin, stdout, stderr) +func newBackupCommand(logdest logger.Logger) *cobra.Command { + cmd := ctl.NewBackupCommand(logdest) ccmd := &cobra.Command{ Use: "backup", Short: "Back up FeatureBase server", diff --git a/cmd/backup_tar.go b/cmd/backup_tar.go index 1c53b5d14..b1309b293 100644 --- a/cmd/backup_tar.go +++ b/cmd/backup_tar.go @@ -2,14 +2,13 @@ package cmd import ( - "io" - "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/spf13/cobra" ) -func newBackupTarCommand(stdin io.Reader, stdout io.Writer, stderr io.Writer) *cobra.Command { - cmd := ctl.NewBackupTarCommand(stdin, stdout, stderr) +func newBackupTarCommand(logdest logger.Logger) *cobra.Command { + cmd := ctl.NewBackupTarCommand(logdest) ccmd := &cobra.Command{ Use: "backuptar", Short: "Back up FeatureBase server in tar format", diff --git a/cmd/chksum.go b/cmd/chksum.go index 78896027a..d073203fc 100644 --- a/cmd/chksum.go +++ b/cmd/chksum.go @@ -2,14 +2,13 @@ package cmd import ( - "io" - "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/spf13/cobra" ) -func newChkSumCommand(stdin io.Reader, stdout io.Writer, stderr io.Writer) *cobra.Command { - cmd := ctl.NewChkSumCommand(stdin, stdout, stderr) +func newChkSumCommand(logdest logger.Logger) *cobra.Command { + cmd := ctl.NewChkSumCommand(logdest) ccmd := &cobra.Command{ Use: "chksum", Short: "Digital signature of FeatureBase data", diff --git a/cmd/cli.go b/cmd/cli.go index 412290770..fd0e8d3e1 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -2,17 +2,16 @@ package cmd import ( - "io" - "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/spf13/cobra" ) var cli *ctl.CLICommand // newCLICommand runs the FeatureBase CLI subcommand for ingesting bulk data. -func newCLICommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - cli = ctl.NewCLICommand(stdin, stdout, stderr) +func newCLICommand(logdest logger.Logger) *cobra.Command { + cli = ctl.NewCLICommand(logdest) cliCmd := &cobra.Command{ Use: "cli", Short: "Query FB with SQL3 from the command line", diff --git a/cmd/config.go b/cmd/config.go index e3167657d..3a1051426 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -13,9 +13,9 @@ import ( var conf *ctl.ConfigCommand -func newConfigCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - conf = ctl.NewConfigCommand(stdin, stdout, stderr) - Server := server.NewCommand(stdin, stdout, stderr) +func newConfigCommand(stderr io.Writer) *cobra.Command { + conf = ctl.NewConfigCommand(stderr) + Server := server.NewCommand(stderr) confCmd := &cobra.Command{ Use: "config", Short: "Print the current configuration.", diff --git a/cmd/dax.go b/cmd/dax.go index 957db266a..52c419940 100644 --- a/cmd/dax.go +++ b/cmd/dax.go @@ -11,8 +11,8 @@ import ( ) // newDAXCommand runs the FeatureBase CLI subcommand for ingesting bulk data. -func newDAXCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - server := server.NewCommand(stdin, stdout, stderr) +func newDAXCommand(stderr io.Writer) *cobra.Command { + server := server.NewCommand(stderr) daxCmd := &cobra.Command{ Use: "dax", Short: "Run a collection of DAX services", diff --git a/cmd/export.go b/cmd/export.go index ec9fbdc4b..c70f04361 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -2,17 +2,16 @@ package cmd import ( - "io" - "github.com/spf13/cobra" "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" ) var Exporter *ctl.ExportCommand -func newExportCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - Exporter = ctl.NewExportCommand(stdin, stdout, stderr) +func newExportCommand(logdest logger.Logger) *cobra.Command { + Exporter = ctl.NewExportCommand(logdest) exportCmd := &cobra.Command{ Use: "export", Short: "Export data from FeatureBase.", diff --git a/cmd/featurebase/main.go b/cmd/featurebase/main.go index c117db8da..fee97f753 100644 --- a/cmd/featurebase/main.go +++ b/cmd/featurebase/main.go @@ -14,7 +14,7 @@ import ( func main() { defer monitor.CaptureMessage("Session:Ended") - rootCmd := cmd.NewRootCommand(os.Stdin, os.Stdout, os.Stderr) + rootCmd := cmd.NewRootCommand(os.Stderr) if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) diff --git a/cmd/generate_config.go b/cmd/generate_config.go index cd5400cd9..e848e5579 100644 --- a/cmd/generate_config.go +++ b/cmd/generate_config.go @@ -2,17 +2,16 @@ package cmd import ( - "io" - "github.com/spf13/cobra" "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" ) var generateConf *ctl.GenerateConfigCommand -func newGenerateConfigCommand(stdin io.Reader, stdout io.Writer, stderr io.Writer) *cobra.Command { - generateConf = ctl.NewGenerateConfigCommand(stdin, stdout, stderr) +func newGenerateConfigCommand(logdest logger.Logger) *cobra.Command { + generateConf = ctl.NewGenerateConfigCommand(logdest) confCmd := &cobra.Command{ Use: "generate-config", Short: "Print the default configuration.", diff --git a/cmd/import.go b/cmd/import.go index 067faa97b..eb6cf2e82 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -4,11 +4,11 @@ package cmd import ( "context" "fmt" - "io" "strconv" pilosa "github.com/molecula/featurebase/v3" "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/pql" "github.com/spf13/cobra" ) @@ -43,8 +43,8 @@ func (dfv *DecimalFlagValue) Type() string { } // newImportCommand runs the FeatureBase import subcommand for ingesting bulk data. -func newImportCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - Importer = ctl.NewImportCommand(stdin, stdout, stderr) +func newImportCommand(logdest logger.Logger) *cobra.Command { + Importer = ctl.NewImportCommand(logdest) importCmd := &cobra.Command{ Use: "import", Short: "Bulk load data into FeatureBase.", diff --git a/cmd/keygen.go b/cmd/keygen.go index a93d2e69c..0f3ea89c5 100644 --- a/cmd/keygen.go +++ b/cmd/keygen.go @@ -2,14 +2,13 @@ package cmd import ( - "io" - "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/spf13/cobra" ) -func newKeygenCommand(stdin io.Reader, stdout io.Writer, stderr io.Writer) *cobra.Command { - cmd := ctl.NewKeygenCommand(stdin, stdout, stderr) +func newKeygenCommand(logdest logger.Logger) *cobra.Command { + cmd := ctl.NewKeygenCommand(logdest) ccmd := &cobra.Command{ Use: "keygen", Short: "Generate secret key for authentication.", diff --git a/cmd/rbf.go b/cmd/rbf.go index ec4207511..173a7f88e 100644 --- a/cmd/rbf.go +++ b/cmd/rbf.go @@ -4,14 +4,14 @@ package cmd import ( "errors" "fmt" - "io" "strconv" "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/spf13/cobra" ) -func newRBFCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { +func newRBFCommand(logdest logger.Logger) *cobra.Command { cmd := &cobra.Command{ Use: "rbf", Short: "Inspect RBF data files.", @@ -19,15 +19,15 @@ func newRBFCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { Provides a set of commands for inspecting RBF data files. `, } - cmd.AddCommand(newRBFCheckCommand(stdin, stdout, stderr)) - cmd.AddCommand(newRBFDumpCommand(stdin, stdout, stderr)) - cmd.AddCommand(newRBFPagesCommand(stdin, stdout, stderr)) - cmd.AddCommand(newRBFPageCommand(stdin, stdout, stderr)) + cmd.AddCommand(newRBFCheckCommand(logdest)) + cmd.AddCommand(newRBFDumpCommand(logdest)) + cmd.AddCommand(newRBFPagesCommand(logdest)) + cmd.AddCommand(newRBFPageCommand(logdest)) return cmd } -func newRBFCheckCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - c := ctl.NewRBFCheckCommand(stdin, stdout, stderr) +func newRBFCheckCommand(logdest logger.Logger) *cobra.Command { + c := ctl.NewRBFCheckCommand(logdest) cmd := &cobra.Command{ Use: "check [flags] PATH", Short: "Run consistency check on RBF data.", @@ -48,8 +48,8 @@ Executes a consistency check on an RBF data directory. return cmd } -func newRBFDumpCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - c := ctl.NewRBFDumpCommand(stdin, stdout, stderr) +func newRBFDumpCommand(logdest logger.Logger) *cobra.Command { + c := ctl.NewRBFDumpCommand(logdest) cmd := &cobra.Command{ Use: "dump [flags] PATH PGNO [PGNO...]", Short: "Prints RBF raw page data", @@ -80,8 +80,8 @@ Dumps the raw hex data for one or more RBF pages. return cmd } -func newRBFPagesCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - c := ctl.NewRBFPagesCommand(stdin, stdout, stderr) +func newRBFPagesCommand(logdest logger.Logger) *cobra.Command { + c := ctl.NewRBFPagesCommand(logdest) cmd := &cobra.Command{ Use: "pages [flags] PATH", Short: "Prints metadata for the list of all pages", @@ -105,8 +105,8 @@ Prints a line for every page in the database with its type/status. return cmd } -func newRBFPageCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - c := ctl.NewRBFPageCommand(stdin, stdout, stderr) +func newRBFPageCommand(logdest logger.Logger) *cobra.Command { + c := ctl.NewRBFPageCommand(logdest) cmd := &cobra.Command{ Use: "page [flags] PATH PGNO [PGNO...]", Short: "Prints data for a page(s)", diff --git a/cmd/restore.go b/cmd/restore.go index 3f1f7b5ec..457ba530d 100644 --- a/cmd/restore.go +++ b/cmd/restore.go @@ -2,14 +2,13 @@ package cmd import ( - "io" - "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/spf13/cobra" ) -func newRestoreCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - cmd := ctl.NewRestoreCommand(stdin, stdout, stderr) +func newRestoreCommand(logdest logger.Logger) *cobra.Command { + cmd := ctl.NewRestoreCommand(logdest) restoreCmd := &cobra.Command{ Use: "restore", Short: "Restore from a backup", diff --git a/cmd/restore_tar.go b/cmd/restore_tar.go index fd65f7f95..d0a05f360 100644 --- a/cmd/restore_tar.go +++ b/cmd/restore_tar.go @@ -2,14 +2,13 @@ package cmd import ( - "io" - "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/spf13/cobra" ) -func newRestoreTarCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - cmd := ctl.NewRestoreTarCommand(stdin, stdout, stderr) +func newRestoreTarCommand(logdest logger.Logger) *cobra.Command { + cmd := ctl.NewRestoreTarCommand(logdest) restoreCmd := &cobra.Command{ Use: "restoretar", Short: "Restore from a backup in tar format", diff --git a/cmd/root.go b/cmd/root.go index 1396c337a..80bc7f5a1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -10,6 +10,7 @@ import ( pilosa "github.com/molecula/featurebase/v3" "github.com/molecula/featurebase/v3/ctl" + "github.com/molecula/featurebase/v3/logger" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -45,7 +46,8 @@ func considerUsageError(cmd *cobra.Command, err error) error { return err } -func NewRootCommand(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { +func NewRootCommand(stderr io.Writer) *cobra.Command { + logdest := logger.NewStandardLogger(stderr) rc := &cobra.Command{ Use: "featurebase", // TODO: These short/long descriptions could use some updating. @@ -87,22 +89,22 @@ at https://docs.molecula.cloud/. _ = rc.PersistentFlags().MarkHidden("dry-run") rc.PersistentFlags().StringP("config", "c", "", "Configuration file to read from.") - rc.AddCommand(newChkSumCommand(stdin, stdout, stderr)) - rc.AddCommand(newBackupCommand(stdin, stdout, stderr)) - rc.AddCommand(newRestoreCommand(stdin, stdout, stderr)) - rc.AddCommand(newBackupTarCommand(stdin, stdout, stderr)) - rc.AddCommand(newRestoreTarCommand(stdin, stdout, stderr)) - rc.AddCommand(newConfigCommand(stdin, stdout, stderr)) - rc.AddCommand(newExportCommand(stdin, stdout, stderr)) - rc.AddCommand(newGenerateConfigCommand(stdin, stdout, stderr)) - rc.AddCommand(newImportCommand(stdin, stdout, stderr)) - rc.AddCommand(newAuthTokenCommand(stdin, stdout, stderr)) - rc.AddCommand(newRBFCommand(stdin, stdout, stderr)) - rc.AddCommand(newServeCmd(stdin, stdout, stderr)) - rc.AddCommand(newHolderCmd(stdin, stdout, stderr)) - rc.AddCommand(newKeygenCommand(stdin, stdout, stderr)) - rc.AddCommand(newCLICommand(stdin, stdout, stderr)) - rc.AddCommand(newDAXCommand(stdin, stdout, stderr)) + rc.AddCommand(newChkSumCommand(logdest)) + rc.AddCommand(newBackupCommand(logdest)) + rc.AddCommand(newRestoreCommand(logdest)) + rc.AddCommand(newBackupTarCommand(logdest)) + rc.AddCommand(newRestoreTarCommand(logdest)) + rc.AddCommand(newConfigCommand(stderr)) + rc.AddCommand(newExportCommand(logdest)) + rc.AddCommand(newGenerateConfigCommand(logdest)) + rc.AddCommand(newImportCommand(logdest)) + rc.AddCommand(newAuthTokenCommand(logdest)) + rc.AddCommand(newRBFCommand(logdest)) + rc.AddCommand(newServeCmd(stderr)) + rc.AddCommand(newHolderCmd(stderr)) + rc.AddCommand(newKeygenCommand(logdest)) + rc.AddCommand(newCLICommand(logdest)) + rc.AddCommand(newDAXCommand(stderr)) rc.SetOutput(stderr) return rc diff --git a/cmd/root_test.go b/cmd/root_test.go index 2ca386a55..703c8835d 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -2,6 +2,7 @@ package cmd_test import ( + "bytes" "fmt" "io" "os" @@ -9,8 +10,6 @@ import ( "strings" "testing" - "time" - "github.com/molecula/featurebase/v3/cmd" "github.com/molecula/featurebase/v3/testhook" "github.com/spf13/cobra" @@ -25,43 +24,15 @@ func failErr(t *testing.T, err error, context ...string) { } } -// tExec executes the given `cmd`, which will be writing its output to `w`, and -// can be read from `out`. It will fail the test if the command does not return -// within 1 second. Useful for testing help messages and such. -func tExec(t *testing.T, cmd *cobra.Command, out io.Reader, w io.WriteCloser) (output []byte, err error) { - done := make(chan struct{}) - var readErr error - go func() { - output, readErr = io.ReadAll(out) - close(done) - }() - err = cmd.Execute() - if err != nil { - return output, err - } - if err := w.Close(); err != nil { - return output, fmt.Errorf("closing cmd's stdout: %v", err) - } - - // NOTE: if cmd.Execute doesn't return, then this select (and - // therefore the one-second timeout, won't be reached) - select { - case <-done: - case <-time.After(time.Second * 1): - t.Fatal("Test failed due to command execution timeout") - } - return output, readErr -} - // ExecNewRootCommand executes the pilosa root command with the given arguments // and returns its output. It will fail if the command does not complete within // 1 second. func ExecNewRootCommand(t *testing.T, args ...string) (string, error) { - out, w := io.Pipe() - rc := cmd.NewRootCommand(os.Stdin, w, w) + buf := &bytes.Buffer{} + rc := cmd.NewRootCommand(buf) rc.SetArgs(args) - output, err := tExec(t, rc, out, w) - return string(output), err + err := rc.Execute() + return buf.String(), err } // validator is a simple helper to avoid repeated `if err != nil` checks in @@ -141,7 +112,7 @@ func (ct *commandTest) setupCommand(t *testing.T) *cobra.Command { os.Setenv("PILOSA_POSTGRES_BIND", "") // make command and set args - rc := cmd.NewRootCommand(strings.NewReader(""), io.Discard, io.Discard) + rc := cmd.NewRootCommand(io.Discard) rc.SetArgs(ct.args) err = cfgFile.Close() diff --git a/cmd/server.go b/cmd/server.go index a0eafb745..fcb0c10e6 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -20,8 +20,8 @@ var holder *server.Command // newHolderCmd creates a FeatureBase server for just long enough to open the // holder, then shuts it down again. -func newHolderCmd(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - holder = server.NewCommand(stdin, stdout, stderr) +func newHolderCmd(stderr io.Writer) *cobra.Command { + holder = server.NewCommand(stderr) serveCmd := &cobra.Command{ Use: "holder", Short: "Load FeatureBase.", @@ -45,8 +45,8 @@ This is only useful for diagnostic use. } // newServeCmd creates a FeatureBase server and runs it with command line flags. -func newServeCmd(stdin io.Reader, stdout, stderr io.Writer) *cobra.Command { - Server = server.NewCommand(stdin, stdout, stderr) +func newServeCmd(stderr io.Writer) *cobra.Command { + Server = server.NewCommand(stderr) serveCmd := &cobra.Command{ Use: "server", Short: "Run FeatureBase.", diff --git a/ctl/auth_token.go b/ctl/auth_token.go index 11ba7c9c3..85d087533 100644 --- a/ctl/auth_token.go +++ b/ctl/auth_token.go @@ -13,6 +13,7 @@ import ( "time" pilosa "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/server" "golang.org/x/oauth2" ) @@ -31,13 +32,18 @@ type AuthTokenCommand struct { // nolint: maligned client *pilosa.InternalClient // Standard input/output. - *pilosa.CmdIO + logDest logger.Logger +} + +// Logger returns the command's associated Logger to maintain CommandWithTLSSupport interface compatibility +func (cmd *AuthTokenCommand) Logger() logger.Logger { + return cmd.logDest } // NewAuthTokenCommand returns a new instance of AuthTokenCommand. -func NewAuthTokenCommand(stdin io.Reader, stdout, stderr io.Writer) *AuthTokenCommand { +func NewAuthTokenCommand(logdest logger.Logger) *AuthTokenCommand { return &AuthTokenCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + logDest: logdest, } } diff --git a/ctl/backup.go b/ctl/backup.go index 0ae3824f4..36d2e4efc 100644 --- a/ctl/backup.go +++ b/ctl/backup.go @@ -18,6 +18,7 @@ import ( "github.com/molecula/featurebase/v3/authn" "github.com/molecula/featurebase/v3/disco" "github.com/molecula/featurebase/v3/encoding/proto" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/server" "github.com/ricochet2200/go-disk-usage/du" "golang.org/x/sync/errgroup" @@ -58,7 +59,7 @@ type BackupCommand struct { // nolint: maligned client *pilosa.InternalClient // Standard input/output - *pilosa.CmdIO + logDest logger.Logger TLS server.TLSConfig @@ -66,10 +67,15 @@ type BackupCommand struct { // nolint: maligned IgnoreSpaceCheck bool } +// Logger returns the command's associated Logger to maintain CommandWithTLSSupport interface compatibility +func (cmd *BackupCommand) Logger() logger.Logger { + return cmd.logDest +} + // NewBackupCommand returns a new instance of BackupCommand. -func NewBackupCommand(stdin io.Reader, stdout, stderr io.Writer) *BackupCommand { +func NewBackupCommand(logdest logger.Logger) *BackupCommand { return &BackupCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + logDest: logdest, Concurrency: 1, RetryPeriod: time.Minute, HeaderTimeout: time.Second * 3, diff --git a/ctl/backup_tar.go b/ctl/backup_tar.go index 5937b6ca4..d89fcb665 100644 --- a/ctl/backup_tar.go +++ b/ctl/backup_tar.go @@ -19,6 +19,7 @@ import ( "github.com/molecula/featurebase/v3/authn" "github.com/molecula/featurebase/v3/disco" "github.com/molecula/featurebase/v3/encoding/proto" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/server" "github.com/molecula/featurebase/v3/vprint" "github.com/pkg/errors" @@ -51,17 +52,22 @@ type BackupTarCommand struct { // nolint: maligned client *pilosa.InternalClient // Standard input/output - *pilosa.CmdIO + logDest logger.Logger TLS server.TLSConfig AuthToken string } +// Logger returns the command's associated Logger to maintain CommandWithTLSSupport interface compatibility +func (cmd *BackupTarCommand) Logger() logger.Logger { + return cmd.logDest +} + // NewBackupTarCommand returns a new instance of BackupCommand. -func NewBackupTarCommand(stdin io.Reader, stdout, stderr io.Writer) *BackupTarCommand { +func NewBackupTarCommand(logdest logger.Logger) *BackupTarCommand { return &BackupTarCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + logDest: logdest, RetryPeriod: time.Minute, HeaderTimeout: time.Second * 3, Pprof: "localhost:0", @@ -130,7 +136,7 @@ func (cmd *BackupTarCommand) Run(ctx context.Context) (err error) { // Create output file in temporary location, or send to stdout if a dash is specified. var w io.Writer if useStdout { - w = cmd.Stdout + w = os.Stdout } else { f, err := os.Create(cmd.OutputPath + ".tmp") if err != nil { diff --git a/ctl/backup_tar_test.go b/ctl/backup_tar_test.go index 2c996d965..80f1cfbf8 100644 --- a/ctl/backup_tar_test.go +++ b/ctl/backup_tar_test.go @@ -1,12 +1,14 @@ package ctl import ( - "bytes" "context" + "io" "net/http" + "path/filepath" "strings" "testing" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/test" ) @@ -15,12 +17,12 @@ func TestBackupTarCommand_Run(t *testing.T) { defer cluster.Close() cmd := cluster.GetNode(0) - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewBackupTarCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewBackupTarCommand(cmLog) hostport := cmd.API.Node().URI.HostPort() cm.Host = hostport - cm.OutputPath = "-" + dir := t.TempDir() + cm.OutputPath = filepath.Join(dir, "backuptest.tar") resp, err := http.DefaultClient.Do(test.MustNewHTTPRequest("POST", "http://"+hostport+"/index/i", strings.NewReader(""))) if err != nil { diff --git a/ctl/backup_test.go b/ctl/backup_test.go index de5718912..8f68b5732 100644 --- a/ctl/backup_test.go +++ b/ctl/backup_test.go @@ -4,12 +4,15 @@ package ctl import ( "context" "errors" - "os" + "io" "testing" + + "github.com/molecula/featurebase/v3/logger" ) func TestBackupCommand_Run(t *testing.T) { - cm := NewBackupCommand(os.Stdin, os.Stdout, os.Stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewBackupCommand(cmLog) cm.OutputDir = "" err := cm.Run(context.Background()) if !errors.Is(err, UsageError) { diff --git a/ctl/chksum.go b/ctl/chksum.go index 037970644..aae733d48 100644 --- a/ctl/chksum.go +++ b/ctl/chksum.go @@ -6,9 +6,11 @@ import ( "crypto/tls" "fmt" "io" + "os" "github.com/cespare/xxhash" pilosa "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/server" ) @@ -23,15 +25,22 @@ type ChkSumCommand struct { // nolint: maligned client *pilosa.InternalClient // Standard input/output - *pilosa.CmdIO + stdout io.Writer + logDest logger.Logger TLS server.TLSConfig } +// Logger returns the command's associated Logger to maintain CommandWithTLSSupport interface compatibility +func (cmd *ChkSumCommand) Logger() logger.Logger { + return cmd.logDest +} + // NewChkSumCommand returns a new instance of BackupCommand. -func NewChkSumCommand(stdin io.Reader, stdout, stderr io.Writer) *ChkSumCommand { +func NewChkSumCommand(logdest logger.Logger) *ChkSumCommand { return &ChkSumCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + stdout: os.Stdout, + logDest: logdest, } } @@ -126,7 +135,7 @@ func (cmd *ChkSumCommand) Run(ctx context.Context) (err error) { } } - fmt.Fprintf(cmd.Stdout, "hash:%x\n", h.Sum(nil)) + fmt.Fprintf(cmd.stdout, "hash:%x\n", h.Sum(nil)) } return nil diff --git a/ctl/chksum_test.go b/ctl/chksum_test.go new file mode 100644 index 000000000..0e8e5f2f9 --- /dev/null +++ b/ctl/chksum_test.go @@ -0,0 +1,31 @@ +// Copyright 2022 Molecula Corp. All rights reserved. +package ctl + +import ( + "bytes" + "context" + "os" + "testing" + + "github.com/molecula/featurebase/v3/logger" + "github.com/molecula/featurebase/v3/test" +) + +func TestChkSumCommand_Run(t *testing.T) { + cmLog := logger.NewStandardLogger(os.Stderr) + cm := NewChkSumCommand(cmLog) + buf := &bytes.Buffer{} + cm.stdout = buf + + cluster := test.MustRunCluster(t, 1) + defer cluster.Close() + cmd := cluster.GetNode(0) + cm.Host = cmd.API.Node().URI.HostPort() + + err := cm.Run(context.Background()) + if err != nil { + t.Fatalf("ChkSum Run doesn't work: %s", err) + } + //ChkSumCommand is only used in executor_test.go, so for now we're just + //making sure that it runs at all, not checking output. +} diff --git a/ctl/cli.go b/ctl/cli.go index 82b212b24..44468b672 100644 --- a/ctl/cli.go +++ b/ctl/cli.go @@ -18,6 +18,7 @@ import ( "github.com/molecula/featurebase/v3/dax" queryerhttp "github.com/molecula/featurebase/v3/dax/queryer/http" "github.com/molecula/featurebase/v3/fbcloud" + "github.com/molecula/featurebase/v3/logger" "github.com/pkg/errors" ) @@ -55,7 +56,7 @@ type CLICommand struct { queryer FBQueryer } -func NewCLICommand(stdin io.Reader, stdout, stderr io.Writer) *CLICommand { +func NewCLICommand(logdest logger.Logger) *CLICommand { historyPath := "" home, err := os.UserHomeDir() if err != nil { diff --git a/ctl/config.go b/ctl/config.go index cd4331e18..d42d2618f 100644 --- a/ctl/config.go +++ b/ctl/config.go @@ -5,22 +5,26 @@ import ( "context" "fmt" "io" + "os" - "github.com/molecula/featurebase/v3" "github.com/molecula/featurebase/v3/server" toml "github.com/pelletier/go-toml" ) // ConfigCommand represents a command for printing a default config. type ConfigCommand struct { - *pilosa.CmdIO + // this exists so we can override it in tests + stdout io.Writer + // this actually gets overridden more generally by different tests + stderr io.Writer Config *server.Config } // NewConfigCommand returns a new instance of ConfigCommand. -func NewConfigCommand(stdin io.Reader, stdout, stderr io.Writer) *ConfigCommand { +func NewConfigCommand(stderr io.Writer) *ConfigCommand { return &ConfigCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + stdout: os.Stdout, + stderr: stderr, } } @@ -30,6 +34,6 @@ func (cmd *ConfigCommand) Run(_ context.Context) error { if err != nil { return err } - fmt.Fprintln(cmd.Stdout, string(buf)) + fmt.Fprintln(cmd.stdout, string(buf)) return nil } diff --git a/ctl/config_test.go b/ctl/config_test.go index 9594229ad..1e9f3e67a 100644 --- a/ctl/config_test.go +++ b/ctl/config_test.go @@ -4,7 +4,6 @@ package ctl import ( "bytes" "context" - "io" "os" "strings" "testing" @@ -13,22 +12,15 @@ import ( ) func TestConfigCommand_Run(t *testing.T) { - rder := []byte{} - stdin := bytes.NewReader(rder) - r, w, _ := os.Pipe() - cm := NewConfigCommand(stdin, w, os.Stderr) + cm := NewConfigCommand(os.Stderr) cm.Config = server.NewConfig() + buf := &bytes.Buffer{} + cm.stdout = buf err := cm.Run(context.Background()) if err != nil { t.Fatalf("Config Run doesn't work: %s", err) } - w.Close() - var buf bytes.Buffer - _, err = io.Copy(&buf, r) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } if !strings.Contains(buf.String(), ":10101") { t.Fatalf("Unexpected config: \n%s", buf.String()) } diff --git a/ctl/export.go b/ctl/export.go index b665bb23b..dc1b1a413 100644 --- a/ctl/export.go +++ b/ctl/export.go @@ -8,6 +8,7 @@ import ( "os" pilosa "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/server" "github.com/pkg/errors" ) @@ -25,15 +26,20 @@ type ExportCommand struct { Path string // Standard input/output - *pilosa.CmdIO + logDest logger.Logger TLS server.TLSConfig } +// Logger returns the command's associated Logger to maintain CommandWithTLSSupport interface compatibility +func (cmd *ExportCommand) Logger() logger.Logger { + return cmd.logDest +} + // NewExportCommand returns a new instance of ExportCommand. -func NewExportCommand(stdin io.Reader, stdout, stderr io.Writer) *ExportCommand { +func NewExportCommand(logdest logger.Logger) *ExportCommand { return &ExportCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + logDest: logdest, } } @@ -50,7 +56,7 @@ func (cmd *ExportCommand) Run(ctx context.Context) error { // Use output file, if specified. // Otherwise use STDOUT. - var w io.Writer = cmd.Stdout + var w io.Writer = os.Stdout if cmd.Path != "" { f, err := os.Create(cmd.Path) if err != nil { @@ -81,13 +87,6 @@ func (cmd *ExportCommand) Run(ctx context.Context) error { } } - // Close writer, if applicable. - if w, ok := w.(io.Closer); ok { - if err := w.Close(); err != nil { - return errors.Wrap(err, "closing") - } - } - return nil } diff --git a/ctl/export_test.go b/ctl/export_test.go index 6b4432caf..b56990ca6 100644 --- a/ctl/export_test.go +++ b/ctl/export_test.go @@ -2,21 +2,22 @@ package ctl import ( - "bytes" "context" + "io" "net/http" + "os" "strings" "testing" pilosa "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/test" ) func TestExportCommand_Validation(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) + cmLog := logger.NewStandardLogger(os.Stderr) - cm := NewExportCommand(stdin, stdout, stderr) + cm := NewExportCommand(cmLog) err := cm.Run(context.Background()) if !errContains(err, pilosa.ErrIndexRequired) { @@ -35,9 +36,8 @@ func TestExportCommand_Run(t *testing.T) { defer cluster.Close() cmd := cluster.GetNode(0) - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewExportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewExportCommand(cmLog) hostport := cmd.API.Node().URI.HostPort() cm.Host = hostport diff --git a/ctl/generate_config.go b/ctl/generate_config.go index 634b69ff9..ccf5c2cdf 100644 --- a/ctl/generate_config.go +++ b/ctl/generate_config.go @@ -5,8 +5,9 @@ import ( "context" "fmt" "io" + "os" - "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/server" "github.com/pelletier/go-toml" "github.com/pkg/errors" @@ -14,13 +15,15 @@ import ( // GenerateConfigCommand represents a command for printing a default config. type GenerateConfigCommand struct { - *pilosa.CmdIO + stdout io.Writer + logDest logger.Logger } // NewGenerateConfigCommand returns a new instance of GenerateConfigCommand. -func NewGenerateConfigCommand(stdin io.Reader, stdout, stderr io.Writer) *GenerateConfigCommand { +func NewGenerateConfigCommand(logdest logger.Logger) *GenerateConfigCommand { return &GenerateConfigCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + stdout: os.Stdout, + logDest: logdest, } } @@ -31,6 +34,6 @@ func (cmd *GenerateConfigCommand) Run(_ context.Context) error { if err != nil { return errors.Wrap(err, "unmarshalling default config") } - fmt.Fprintf(cmd.Stdout, "%s\n", ret) + fmt.Fprintf(cmd.stdout, "%s\n", ret) return nil } diff --git a/ctl/generate_config_test.go b/ctl/generate_config_test.go index f06fee314..ffcf375be 100644 --- a/ctl/generate_config_test.go +++ b/ctl/generate_config_test.go @@ -4,27 +4,22 @@ package ctl import ( "bytes" "context" - "io" "os" "strings" "testing" + + "github.com/molecula/featurebase/v3/logger" ) func TestGenerateConfigCommand_Run(t *testing.T) { - rder := []byte{} - stdin := bytes.NewReader(rder) - r, w, _ := os.Pipe() - cm := NewGenerateConfigCommand(stdin, w, os.Stderr) + cmLog := logger.NewStandardLogger(os.Stderr) + cm := NewGenerateConfigCommand(cmLog) + buf := &bytes.Buffer{} + cm.stdout = buf err := cm.Run(context.Background()) if err != nil { t.Fatalf("Config Run doesn't work: %s", err) } - w.Close() - var buf bytes.Buffer - _, err = io.Copy(&buf, r) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } if !strings.Contains(buf.String(), ":10101") { t.Fatalf("Unexpected config: %s", buf.String()) } diff --git a/ctl/import.go b/ctl/import.go index 62ba4605b..af23be7b2 100644 --- a/ctl/import.go +++ b/ctl/import.go @@ -13,6 +13,7 @@ import ( pilosa "github.com/molecula/featurebase/v3" "github.com/molecula/featurebase/v3/authn" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/pql" "github.com/molecula/featurebase/v3/server" "github.com/pkg/errors" @@ -57,17 +58,22 @@ type ImportCommand struct { // nolint: maligned client *pilosa.InternalClient // Standard input/output - *pilosa.CmdIO + logDest logger.Logger TLS server.TLSConfig AuthToken string } +// Logger returns the command's associated Logger to maintain CommandWithTLSSupport interface compatibility +func (cmd *ImportCommand) Logger() logger.Logger { + return cmd.logDest +} + // NewImportCommand returns a new instance of ImportCommand. -func NewImportCommand(stdin io.Reader, stdout, stderr io.Writer) *ImportCommand { +func NewImportCommand(logdest logger.Logger) *ImportCommand { return &ImportCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + logDest: logdest, BufferSize: 10000000, } } @@ -184,7 +190,7 @@ func (cmd *ImportCommand) bufferBits(ctx context.Context, useColumnKeys, useRowK // Read rows as bits. r = csv.NewReader(f) } else { - r = csv.NewReader(cmd.Stdin) + r = csv.NewReader(os.Stdin) } r.FieldsPerRecord = -1 @@ -306,7 +312,7 @@ func (cmd *ImportCommand) bufferValues(ctx context.Context, useColumnKeys, parse // Read rows as bits. r = csv.NewReader(f) } else { - r = csv.NewReader(cmd.Stdin) + r = csv.NewReader(os.Stdin) } r.FieldsPerRecord = -1 diff --git a/ctl/import_test.go b/ctl/import_test.go index e30cb7960..431329462 100644 --- a/ctl/import_test.go +++ b/ctl/import_test.go @@ -33,10 +33,7 @@ import ( // and a non-nil error does not contain a nil error. func errContains(err error, expected error) bool { if err == nil { - if expected == nil { - return true - } - return false + return expected == nil } if expected == nil { return false @@ -48,9 +45,8 @@ func errContains(err error, expected error) bool { return strings.Contains(e1, e2) } func TestImportCommand_Validation(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) err := cm.Run(context.Background()) if !errContains(err, pilosa.ErrIndexRequired) { t.Fatalf("wrong error: expected %q, got: '%v'", pilosa.ErrIndexRequired, err) @@ -72,9 +68,8 @@ func TestImportCommand_Validation(t *testing.T) { func TestImportCommand_Basic(t *testing.T) { t.Run("set", func(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) file, err := testhook.TempFile(t, "import.csv") if err != nil { t.Fatalf("creating tempfile: %v", err) @@ -104,9 +99,8 @@ func TestImportCommand_Basic(t *testing.T) { }) t.Run("clear", func(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) file, err := testhook.TempFile(t, "import.csv") if err != nil { t.Fatalf("creating tempfile: %v", err) @@ -137,9 +131,8 @@ func TestImportCommand_Basic(t *testing.T) { // Ensure that the ImportValue path runs. func TestImportCommand_RunValue(t *testing.T) { t.Run("set", func(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) file, err := testhook.TempFile(t, "import-value.csv") if err != nil { t.Fatalf("creating tempfile: %v", err) @@ -176,9 +169,8 @@ func TestImportCommand_RunValue(t *testing.T) { }) t.Run("clear", func(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) file, err := testhook.TempFile(t, "import-value.csv") if err != nil { t.Fatalf("creating tempfile: %v", err) @@ -221,9 +213,8 @@ func TestImportCommand_RunValue(t *testing.T) { // Ensure that import with keys runs. func TestImportCommand_RunKeys(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) file, err := testhook.TempFile(t, "import-key.csv") if err != nil { t.Fatal(err) @@ -261,9 +252,8 @@ func TestImportCommand_RunKeys(t *testing.T) { // Ensure that import with keys runs with key replication. func TestImportCommand_KeyReplication(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) file, err := testhook.TempFile(t, "import-key.csv") if err != nil { t.Fatal(err) @@ -339,9 +329,8 @@ func TestImportCommand_KeyReplication(t *testing.T) { // Ensure that integer import with keys runs. func TestImportCommand_RunValueKeys(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) file, err := testhook.TempFile(t, "import-key.csv") if err != nil { t.Fatal(err) @@ -382,9 +371,8 @@ func TestImportCommand_InvalidFile(t *testing.T) { defer cluster.Close() cmd := cluster.GetNode(0) - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) cm.Host = cmd.API.Node().URI.HostPort() cm.Index = "i" cm.Field = "f" @@ -470,9 +458,8 @@ func TestImportCommand_BugOverwriteValue(t *testing.T) { defer cluster.Close() cmd := cluster.GetNode(0) - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) file, err := testhook.TempFile(t, "import-value.csv") if err != nil { t.Fatal(err) @@ -537,9 +524,8 @@ func TestImportCommand_BugOverwriteValue(t *testing.T) { // Ensure that import into bool field runs. func TestImportCommand_RunBool(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) ctx := context.Background() cluster := test.MustRunCluster(t, 1) @@ -717,9 +703,8 @@ func TestImport_AuthOn(t *testing.T) { } t.Run("set", func(t *testing.T) { - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewImportCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewImportCommand(cmLog) file, err := testhook.TempFile(t, "import.csv") if err != nil { t.Fatalf("creating tempfile: %v", err) diff --git a/ctl/keygen.go b/ctl/keygen.go index 19b4f8026..357966e2e 100644 --- a/ctl/keygen.go +++ b/ctl/keygen.go @@ -5,26 +5,29 @@ import ( "context" "fmt" "io" + "os" "github.com/gorilla/securecookie" - pilosa "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" ) // Keygen represents a command for generating a cryptographic key. type KeygenCommand struct { - CmdIO *pilosa.CmdIO + stdout io.Writer + logDest logger.Logger KeyLength int } // NewKeygen returns a new instance of Keygen. -func NewKeygenCommand(stdin io.Reader, stdout, stderr io.Writer) *KeygenCommand { +func NewKeygenCommand(logdest logger.Logger) *KeygenCommand { return &KeygenCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + stdout: os.Stdout, + logDest: logdest, } } // Run keygen to obtain key to use for authentication . func (kg *KeygenCommand) Run(_ context.Context) error { - fmt.Printf("secret-key = \"%+x\"\n", securecookie.GenerateRandomKey(kg.KeyLength)) + fmt.Fprintf(kg.stdout, "secret-key = \"%+x\"\n", securecookie.GenerateRandomKey(kg.KeyLength)) return nil } diff --git a/ctl/keygen_test.go b/ctl/keygen_test.go new file mode 100644 index 000000000..860ba0251 --- /dev/null +++ b/ctl/keygen_test.go @@ -0,0 +1,26 @@ +// Copyright 2022 Molecula Corp. All rights reserved. +package ctl + +import ( + "bytes" + "context" + "os" + "strings" + "testing" + + "github.com/molecula/featurebase/v3/logger" +) + +func TestKeygenCommand_Run(t *testing.T) { + cmLog := logger.NewStandardLogger(os.Stderr) + cm := NewKeygenCommand(cmLog) + buf := &bytes.Buffer{} + cm.stdout = buf + err := cm.Run(context.Background()) + if err != nil { + t.Fatalf("Keygen Run doesn't work: %s", err) + } + if !strings.Contains(buf.String(), "secret-key =") { + t.Fatalf("Unexpected output: %s", buf.String()) + } +} diff --git a/ctl/rbf_check.go b/ctl/rbf_check.go index cc6ec3465..926d61936 100644 --- a/ctl/rbf_check.go +++ b/ctl/rbf_check.go @@ -5,8 +5,9 @@ import ( "context" "fmt" "io" + "os" - "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/rbf" ) @@ -16,13 +17,15 @@ type RBFCheckCommand struct { Path string // Standard input/output - *pilosa.CmdIO + stdout io.Writer + logDest logger.Logger } // NewRBFCheckCommand returns a new instance of RBFCheckCommand. -func NewRBFCheckCommand(stdin io.Reader, stdout, stderr io.Writer) *RBFCheckCommand { +func NewRBFCheckCommand(logdest logger.Logger) *RBFCheckCommand { return &RBFCheckCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + stdout: os.Stdout, + logDest: logdest, } } @@ -40,16 +43,16 @@ func (cmd *RBFCheckCommand) Run(ctx context.Context) error { switch err := err.(type) { case rbf.ErrorList: for i := range err { - fmt.Fprintln(cmd.Stdout, err[i]) + fmt.Fprintln(cmd.stdout, err[i]) } default: - fmt.Fprintln(cmd.Stdout, err) + fmt.Fprintln(cmd.stdout, err) } return fmt.Errorf("check failed") } // If successful, print a success message. - fmt.Fprintln(cmd.Stdout, "ok") + fmt.Fprintln(cmd.stdout, "ok") return nil } diff --git a/ctl/rbf_check_test.go b/ctl/rbf_check_test.go index c11cd30dc..9cc9c52cf 100644 --- a/ctl/rbf_check_test.go +++ b/ctl/rbf_check_test.go @@ -4,29 +4,36 @@ package ctl import ( "bytes" "context" + "os" "path/filepath" "testing" + + "github.com/molecula/featurebase/v3/logger" ) func TestRBFCheckCommand_Run(t *testing.T) { t.Run("OK", func(t *testing.T) { - var stdout, stderr bytes.Buffer - cmd := NewRBFCheckCommand(bytes.NewReader(nil), &stdout, &stderr) - cmd.Path = filepath.Join("testdata", "rbf-check", "ok") + cmLog := logger.NewStandardLogger(os.Stderr) + cmd := NewRBFCheckCommand(cmLog) + buf := &bytes.Buffer{} + cmd.stdout = buf + cmd.Path = filepath.Join("testdata", "ok") if err := cmd.Run(context.Background()); err != nil { t.Fatal(err) - } else if got, want := stdout.String(), `ok`+"\n"; got != want { + } else if got, want := buf.String(), `ok`+"\n"; got != want { t.Fatalf("got:\n%s\n\nwant:\n%s", got, want) } }) t.Run("ErrInvalidPageType", func(t *testing.T) { - var stdout, stderr bytes.Buffer - cmd := NewRBFCheckCommand(bytes.NewReader(nil), &stdout, &stderr) - cmd.Path = filepath.Join("testdata", "rbf-check", "err-invalid-page-type") + cmLog := logger.NewStandardLogger(os.Stderr) + cmd := NewRBFCheckCommand(cmLog) + buf := &bytes.Buffer{} + cmd.stdout = buf + cmd.Path = filepath.Join("testdata", "err-invalid-page-type") if err := cmd.Run(context.Background()); err == nil || err.Error() != `check failed` { t.Fatal(err) - } else if got, want := stdout.String(), `page not in-use & not free: pgno=4`+"\n"; got != want { + } else if got, want := buf.String(), `page not in-use & not free: pgno=4`+"\n"; got != want { t.Fatalf("got:\n%s\n\nwant:\n%s", got, want) } }) diff --git a/ctl/rbf_dump.go b/ctl/rbf_dump.go index cfaa120e1..7b6f07422 100644 --- a/ctl/rbf_dump.go +++ b/ctl/rbf_dump.go @@ -6,9 +6,10 @@ import ( "encoding/hex" "fmt" "io" + "os" "strings" - "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/rbf" ) @@ -21,13 +22,15 @@ type RBFDumpCommand struct { Pgnos []uint32 // Standard input/output - *pilosa.CmdIO + stdout io.Writer + logDest logger.Logger } // NewRBFDumpCommand returns a new instance of RBFDumpCommand. -func NewRBFDumpCommand(stdin io.Reader, stdout, stderr io.Writer) *RBFDumpCommand { +func NewRBFDumpCommand(logdest logger.Logger) *RBFDumpCommand { return &RBFDumpCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + stdout: os.Stdout, + logDest: logdest, } } @@ -54,9 +57,9 @@ func (cmd *RBFDumpCommand) Run(ctx context.Context) error { return err } - fmt.Fprintf(cmd.Stdout, "## PAGE %d\n", pgno) - fmt.Fprintln(cmd.Stdout, compressedHexDump(buf)) - fmt.Fprintln(cmd.Stdout, "") + fmt.Fprintf(cmd.stdout, "## PAGE %d\n", pgno) + fmt.Fprintln(cmd.stdout, compressedHexDump(buf)) + fmt.Fprintln(cmd.stdout, "") } return nil diff --git a/ctl/rbf_dump_test.go b/ctl/rbf_dump_test.go new file mode 100644 index 000000000..847c2798c --- /dev/null +++ b/ctl/rbf_dump_test.go @@ -0,0 +1,45 @@ +// Copyright 2022 Molecula Corp. All rights reserved. +package ctl + +import ( + "bytes" + "context" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/molecula/featurebase/v3/logger" +) + +func TestRBFDumpCommand_Run(t *testing.T) { + t.Run("OK", func(t *testing.T) { + cmLog := logger.NewStandardLogger(os.Stderr) + cmd := NewRBFDumpCommand(cmLog) + buf := &bytes.Buffer{} + cmd.stdout = buf + cmd.Path = filepath.Join("testdata", "ok") + cmd.Pgnos = []uint32{0} + if err := cmd.Run(context.Background()); err != nil { + t.Fatal(err) + } else if got, want := buf.String(), "## PAGE 0"; !strings.Contains(got, want) { + t.Fatalf("got:\n%s\n\nwant:\n%s", got, want) + } + }) + + t.Run("ErrInvalidPageType", func(t *testing.T) { + t.Skip("Need to figure out how to get an error and what the error message will be.") + cmLog := logger.NewStandardLogger(os.Stderr) + cmd := NewRBFDumpCommand(cmLog) + buf := &bytes.Buffer{} + cmd.stdout = buf + cmd.Path = filepath.Join("testdata", "err-invalid-page-type") + cmd.Pgnos = []uint32{4} + if err := cmd.Run(context.Background()); err == nil || err.Error() != `check failed` { + t.Fatal(err) + //this part is still from rbf_check_test til i figure out what the actual error output should be + } else if got, want := buf.String(), `page not in-use & not free: pgno=4`+"\n"; got != want { + t.Fatalf("got:\n%s\n\nwant:\n%s", got, want) + } + }) +} diff --git a/ctl/rbf_page.go b/ctl/rbf_page.go index eb0a71c7f..4935891c1 100644 --- a/ctl/rbf_page.go +++ b/ctl/rbf_page.go @@ -5,8 +5,9 @@ import ( "context" "fmt" "io" + "os" - "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/rbf" ) @@ -19,13 +20,15 @@ type RBFPageCommand struct { Pgnos []uint32 // Standard input/output - *pilosa.CmdIO + stdout io.Writer + logDest logger.Logger } // NewRBFPageCommand returns a new instance of RBFPageCommand. -func NewRBFPageCommand(stdin io.Reader, stdout, stderr io.Writer) *RBFPageCommand { +func NewRBFPageCommand(logdest logger.Logger) *RBFPageCommand { return &RBFPageCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + stdout: os.Stdout, + logDest: logdest, } } @@ -68,60 +71,60 @@ func (cmd *RBFPageCommand) Run(ctx context.Context) error { default: return fmt.Errorf("unexpected page type %T", page) } - fmt.Fprintln(cmd.Stdout, "") + fmt.Fprintln(cmd.stdout, "") } return nil } func (cmd *RBFPageCommand) printMetaPage(page *rbf.MetaPage) { - fmt.Fprintf(cmd.Stdout, "Pgno: %d\n", page.Pgno) - fmt.Fprintf(cmd.Stdout, "Type: meta\n") - fmt.Fprintf(cmd.Stdout, "PageN: %d\n", page.PageN) - fmt.Fprintf(cmd.Stdout, "WALID: %d\n", page.WALID) - fmt.Fprintf(cmd.Stdout, "Root Record Pgno: %d\n", page.RootRecordPageNo) - fmt.Fprintf(cmd.Stdout, "Freelist Pgno: %d\n", page.FreelistPageNo) + fmt.Fprintf(cmd.stdout, "Pgno: %d\n", page.Pgno) + fmt.Fprintf(cmd.stdout, "Type: meta\n") + fmt.Fprintf(cmd.stdout, "PageN: %d\n", page.PageN) + fmt.Fprintf(cmd.stdout, "WALID: %d\n", page.WALID) + fmt.Fprintf(cmd.stdout, "Root Record Pgno: %d\n", page.RootRecordPageNo) + fmt.Fprintf(cmd.stdout, "Freelist Pgno: %d\n", page.FreelistPageNo) } func (cmd *RBFPageCommand) printRootRecordPage(page *rbf.RootRecordPage) { - fmt.Fprintf(cmd.Stdout, "Pgno: %d\n", page.Pgno) - fmt.Fprintf(cmd.Stdout, "Type: root record\n") - fmt.Fprintf(cmd.Stdout, "Next: %d\n", page.Next) - fmt.Fprintf(cmd.Stdout, "Records: n=%d\n", len(page.Records)) + fmt.Fprintf(cmd.stdout, "Pgno: %d\n", page.Pgno) + fmt.Fprintf(cmd.stdout, "Type: root record\n") + fmt.Fprintf(cmd.stdout, "Next: %d\n", page.Next) + fmt.Fprintf(cmd.stdout, "Records: n=%d\n", len(page.Records)) for i, rec := range page.Records { - fmt.Fprintf(cmd.Stdout, "[%d]: name=%q pgno=%d\n", i, rec.Name, rec.Pgno) + fmt.Fprintf(cmd.stdout, "[%d]: name=%q pgno=%d\n", i, rec.Name, rec.Pgno) } } func (cmd *RBFPageCommand) printLeafPage(page *rbf.LeafPage) { - fmt.Fprintf(cmd.Stdout, "Pgno: %d\n", page.Pgno) - fmt.Fprintf(cmd.Stdout, "Type: leaf\n") - fmt.Fprintf(cmd.Stdout, "Cells: n=%d\n", len(page.Cells)) + fmt.Fprintf(cmd.stdout, "Pgno: %d\n", page.Pgno) + fmt.Fprintf(cmd.stdout, "Type: leaf\n") + fmt.Fprintf(cmd.stdout, "Cells: n=%d\n", len(page.Cells)) for i, cell := range page.Cells { if cell.Type == rbf.ContainerTypeBitmapPtr { - fmt.Fprintf(cmd.Stdout, "[%d]: key=%d type=%s pgno=%d\n", i, cell.Key, cell.Type, cell.Pgno) + fmt.Fprintf(cmd.stdout, "[%d]: key=%d type=%s pgno=%d\n", i, cell.Key, cell.Type, cell.Pgno) } else { - fmt.Fprintf(cmd.Stdout, "[%d]: key=%d type=%s values=%v\n", i, cell.Key, cell.Type, cell.Values) + fmt.Fprintf(cmd.stdout, "[%d]: key=%d type=%s values=%v\n", i, cell.Key, cell.Type, cell.Values) } } } func (cmd *RBFPageCommand) printBranchPage(page *rbf.BranchPage) { - fmt.Fprintf(cmd.Stdout, "Pgno: %d\n", page.Pgno) - fmt.Fprintf(cmd.Stdout, "Type: branch\n") - fmt.Fprintf(cmd.Stdout, "Cells: n=%d\n", len(page.Cells)) + fmt.Fprintf(cmd.stdout, "Pgno: %d\n", page.Pgno) + fmt.Fprintf(cmd.stdout, "Type: branch\n") + fmt.Fprintf(cmd.stdout, "Cells: n=%d\n", len(page.Cells)) for i, cell := range page.Cells { - fmt.Fprintf(cmd.Stdout, "[%d]: key=%d flags=%d pgno=%d\n", i, cell.Key, cell.Flags, cell.Pgno) + fmt.Fprintf(cmd.stdout, "[%d]: key=%d flags=%d pgno=%d\n", i, cell.Key, cell.Flags, cell.Pgno) } } func (cmd *RBFPageCommand) printBitmapPage(page *rbf.BitmapPage) { - fmt.Fprintf(cmd.Stdout, "Pgno: %d\n", page.Pgno) - fmt.Fprintf(cmd.Stdout, "Type: bitmap\n") - fmt.Fprintf(cmd.Stdout, "Values: %v\n", page.Values) + fmt.Fprintf(cmd.stdout, "Pgno: %d\n", page.Pgno) + fmt.Fprintf(cmd.stdout, "Type: bitmap\n") + fmt.Fprintf(cmd.stdout, "Values: %v\n", page.Values) } func (cmd *RBFPageCommand) printFreePage(page *rbf.FreePage) { - fmt.Fprintf(cmd.Stdout, "Pgno: %d\n", page.Pgno) - fmt.Fprintf(cmd.Stdout, "Type: free\n") + fmt.Fprintf(cmd.stdout, "Pgno: %d\n", page.Pgno) + fmt.Fprintf(cmd.stdout, "Type: free\n") } diff --git a/ctl/rbf_page_test.go b/ctl/rbf_page_test.go new file mode 100644 index 000000000..4bcf4add2 --- /dev/null +++ b/ctl/rbf_page_test.go @@ -0,0 +1,44 @@ +// Copyright 2022 Molecula Corp. All rights reserved. +package ctl + +import ( + "bytes" + "context" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/molecula/featurebase/v3/logger" +) + +func TestRBFPageCommand_Run(t *testing.T) { + t.Run("OK", func(t *testing.T) { + cmLog := logger.NewStandardLogger(os.Stderr) + cmd := NewRBFPageCommand(cmLog) + buf := &bytes.Buffer{} + cmd.stdout = buf + cmd.Path = filepath.Join("testdata", "ok") + cmd.Pgnos = []uint32{0} + if err := cmd.Run(context.Background()); err != nil { + t.Fatal(err) + } else if got, want := buf.String(), "Type: meta"; !strings.Contains(got, want) { + t.Fatalf("got:\n%s\n\nwant:\n%s", got, want) + } + }) + + t.Run("ErrInvalidPageType", func(t *testing.T) { + t.Skip("RBF panics if we do this") + cmLog := logger.NewStandardLogger(os.Stderr) + cmd := NewRBFPageCommand(cmLog) + buf := &bytes.Buffer{} + cmd.stdout = buf + cmd.Path = filepath.Join("testdata", "err-invalid-page-type") + cmd.Pgnos = []uint32{4} + if err := cmd.Run(context.Background()); err == nil || err.Error() != `check failed` { + t.Fatal(err) + } else if got, want := buf.String(), `page not in-use & not free: pgno=4`+"\n"; got != want { + t.Fatalf("got:\n%s\n\nwant:\n%s", got, want) + } + }) +} diff --git a/ctl/rbf_pages.go b/ctl/rbf_pages.go index 75c6ba59a..ce7cc2689 100644 --- a/ctl/rbf_pages.go +++ b/ctl/rbf_pages.go @@ -5,8 +5,9 @@ import ( "context" "fmt" "io" + "os" - "github.com/molecula/featurebase/v3" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/rbf" "github.com/molecula/featurebase/v3/txkey" ) @@ -20,13 +21,15 @@ type RBFPagesCommand struct { WithTree bool // Standard input/output - *pilosa.CmdIO + stdout io.Writer + logDest logger.Logger } // NewRBFPagesCommand returns a new instance of RBFPagesCommand. -func NewRBFPagesCommand(stdin io.Reader, stdout, stderr io.Writer) *RBFPagesCommand { +func NewRBFPagesCommand(logdest logger.Logger) *RBFPagesCommand { return &RBFPagesCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + stdout: os.Stdout, + logDest: logdest, } } @@ -49,81 +52,81 @@ func (cmd *RBFPagesCommand) Run(ctx context.Context) error { // Iterate over each page and grab info. infos, err := tx.PageInfos() if err != nil { - fmt.Fprintln(cmd.Stdout, "ERRORS:") + fmt.Fprintln(cmd.stdout, "ERRORS:") switch err := err.(type) { case rbf.ErrorList: for i := range err { - fmt.Fprintln(cmd.Stdout, err[i]) + fmt.Fprintln(cmd.stdout, err[i]) } default: - fmt.Fprintln(cmd.Stdout, err) + fmt.Fprintln(cmd.stdout, err) } - fmt.Fprintln(cmd.Stdout, "") + fmt.Fprintln(cmd.stdout, "") } // Write header. - fmt.Fprint(cmd.Stdout, "ID ") - fmt.Fprint(cmd.Stdout, "TYPE ") + fmt.Fprint(cmd.stdout, "ID ") + fmt.Fprint(cmd.stdout, "TYPE ") if cmd.WithTree { - fmt.Fprint(cmd.Stdout, "TREE ") + fmt.Fprint(cmd.stdout, "TREE ") } - fmt.Fprintln(cmd.Stdout, "EXTRA") + fmt.Fprintln(cmd.stdout, "EXTRA") - fmt.Fprint(cmd.Stdout, "======== ") - fmt.Fprint(cmd.Stdout, "========== ") + fmt.Fprint(cmd.stdout, "======== ") + fmt.Fprint(cmd.stdout, "========== ") if cmd.WithTree { - fmt.Fprint(cmd.Stdout, "============================== ") + fmt.Fprint(cmd.stdout, "============================== ") } - fmt.Fprintln(cmd.Stdout, "====================") + fmt.Fprintln(cmd.stdout, "====================") // Print one line for each page. for pgno, info := range infos { - fmt.Fprintf(cmd.Stdout, "%-8d ", pgno) + fmt.Fprintf(cmd.stdout, "%-8d ", pgno) switch info := info.(type) { case *rbf.MetaPageInfo: - fmt.Fprintf(cmd.Stdout, "%-10s ", "meta") + fmt.Fprintf(cmd.stdout, "%-10s ", "meta") if cmd.WithTree { - fmt.Fprintf(cmd.Stdout, "%-30q ", "") + fmt.Fprintf(cmd.stdout, "%-30q ", "") } - fmt.Fprintf(cmd.Stdout, "pageN=%d,walid=%d,rootrec=%d,freelist=%d\n", info.PageN, info.WALID, info.RootRecordPageNo, info.FreelistPageNo) + fmt.Fprintf(cmd.stdout, "pageN=%d,walid=%d,rootrec=%d,freelist=%d\n", info.PageN, info.WALID, info.RootRecordPageNo, info.FreelistPageNo) case *rbf.RootRecordPageInfo: - fmt.Fprintf(cmd.Stdout, "%-10s ", "rootrec") + fmt.Fprintf(cmd.stdout, "%-10s ", "rootrec") if cmd.WithTree { - fmt.Fprintf(cmd.Stdout, "%-30q ", "") + fmt.Fprintf(cmd.stdout, "%-30q ", "") } - fmt.Fprintf(cmd.Stdout, "next=%d\n", info.Next) + fmt.Fprintf(cmd.stdout, "next=%d\n", info.Next) case *rbf.LeafPageInfo: - fmt.Fprintf(cmd.Stdout, "%-10s ", "leaf") + fmt.Fprintf(cmd.stdout, "%-10s ", "leaf") if cmd.WithTree { - fmt.Fprintf(cmd.Stdout, "%-30q ", prefixToString(info.Tree)) + fmt.Fprintf(cmd.stdout, "%-30q ", prefixToString(info.Tree)) } - fmt.Fprintf(cmd.Stdout, "flags=x%x,celln=%d\n", info.Flags, info.CellN) + fmt.Fprintf(cmd.stdout, "flags=x%x,celln=%d\n", info.Flags, info.CellN) case *rbf.BranchPageInfo: - fmt.Fprintf(cmd.Stdout, "%-10s ", "branch") + fmt.Fprintf(cmd.stdout, "%-10s ", "branch") if cmd.WithTree { - fmt.Fprintf(cmd.Stdout, "%-30q ", prefixToString(info.Tree)) + fmt.Fprintf(cmd.stdout, "%-30q ", prefixToString(info.Tree)) } - fmt.Fprintf(cmd.Stdout, "flags=x%x,celln=%d\n", info.Flags, info.CellN) + fmt.Fprintf(cmd.stdout, "flags=x%x,celln=%d\n", info.Flags, info.CellN) case *rbf.BitmapPageInfo: - fmt.Fprintf(cmd.Stdout, "%-10s ", "bitmap") + fmt.Fprintf(cmd.stdout, "%-10s ", "bitmap") if cmd.WithTree { - fmt.Fprintf(cmd.Stdout, "%-30q ", prefixToString(info.Tree)) + fmt.Fprintf(cmd.stdout, "%-30q ", prefixToString(info.Tree)) } - fmt.Fprintf(cmd.Stdout, "-\n") + fmt.Fprintf(cmd.stdout, "-\n") case *rbf.FreePageInfo: - fmt.Fprintf(cmd.Stdout, "%-10s ", "free") + fmt.Fprintf(cmd.stdout, "%-10s ", "free") if cmd.WithTree { - fmt.Fprintf(cmd.Stdout, "%-30q ", "") + fmt.Fprintf(cmd.stdout, "%-30q ", "") } - fmt.Fprintf(cmd.Stdout, "-\n") + fmt.Fprintf(cmd.stdout, "-\n") default: - fmt.Fprintf(cmd.Stdout, "unknown [%T]\n", info) + fmt.Fprintf(cmd.stdout, "unknown [%T]\n", info) } } diff --git a/ctl/rbf_pages_test.go b/ctl/rbf_pages_test.go index 73c395e1b..a85692a44 100644 --- a/ctl/rbf_pages_test.go +++ b/ctl/rbf_pages_test.go @@ -4,8 +4,11 @@ package ctl import ( "bytes" "context" + "os" "path/filepath" "testing" + + "github.com/molecula/featurebase/v3/logger" ) func TestRBFPagesCommand_Run(t *testing.T) { @@ -19,12 +22,14 @@ ID TYPE EXTRA 3 leaf flags=x2,celln=1 `[1:] - var stdout, stderr bytes.Buffer - cmd := NewRBFPagesCommand(bytes.NewReader(nil), &stdout, &stderr) - cmd.Path = filepath.Join("testdata", "rbf-pages", "ok") + cmLog := logger.NewStandardLogger(os.Stderr) + cmd := NewRBFPagesCommand(cmLog) + buf := &bytes.Buffer{} + cmd.stdout = buf + cmd.Path = filepath.Join("testdata", "ok") if err := cmd.Run(context.Background()); err != nil { t.Fatal(err) - } else if got := stdout.String(); got != want { + } else if got := buf.String(); got != want { t.Fatalf("got:\n%s\n\nwant:\n%s", got, want) } }) @@ -40,12 +45,14 @@ ID TYPE EXTRA 4 unknown [] `[1:] - var stdout, stderr bytes.Buffer - cmd := NewRBFPagesCommand(bytes.NewReader(nil), &stdout, &stderr) - cmd.Path = filepath.Join("testdata", "rbf-pages", "err-invalid-page-type") + cmLog := logger.NewStandardLogger(os.Stderr) + cmd := NewRBFPagesCommand(cmLog) + buf := &bytes.Buffer{} + cmd.stdout = buf + cmd.Path = filepath.Join("testdata", "err-invalid-page-type") if err := cmd.Run(context.Background()); err != nil { t.Fatal(err) - } else if got := stdout.String(); got != want { + } else if got := buf.String(); got != want { t.Fatalf("got:\n%s\n\nwant:\n%s", got, want) } }) diff --git a/ctl/restore.go b/ctl/restore.go index b225e0223..47aa2ebf5 100644 --- a/ctl/restore.go +++ b/ctl/restore.go @@ -49,17 +49,22 @@ type RestoreCommand struct { client *pilosa.InternalClient // Standard input/output - *pilosa.CmdIO + logDest logger.Logger TLS server.TLSConfig AuthToken string } +// Logger returns the command's associated Logger to maintain CommandWithTLSSupport interface compatibility +func (cmd *RestoreCommand) Logger() logger.Logger { + return cmd.logDest +} + // NewRestoreCommand returns a new instance of RestoreCommand. -func NewRestoreCommand(stdin io.Reader, stdout, stderr io.Writer) *RestoreCommand { +func NewRestoreCommand(logdest logger.Logger) *RestoreCommand { return &RestoreCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + logDest: logdest, RetryPeriod: time.Second * 30, Concurrency: 1, Pprof: "localhost:0", diff --git a/ctl/restore_tar.go b/ctl/restore_tar.go index eb6606edb..dbd14a636 100644 --- a/ctl/restore_tar.go +++ b/ctl/restore_tar.go @@ -18,6 +18,7 @@ import ( pilosa "github.com/molecula/featurebase/v3" "github.com/molecula/featurebase/v3/authn" "github.com/molecula/featurebase/v3/disco" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/server" "github.com/pkg/errors" "golang.org/x/sync/errgroup" @@ -42,17 +43,22 @@ type RestoreTarCommand struct { client *pilosa.InternalClient // Standard input/output - *pilosa.CmdIO + logDest logger.Logger TLS server.TLSConfig AuthToken string } +// Logger returns the command's associated Logger to maintain CommandWithTLSSupport interface compatibility +func (cmd *RestoreTarCommand) Logger() logger.Logger { + return cmd.logDest +} + // NewRestoreTarCommand returns a new instance of RestoreTarCommand. -func NewRestoreTarCommand(stdin io.Reader, stdout, stderr io.Writer) *RestoreTarCommand { +func NewRestoreTarCommand(logdest logger.Logger) *RestoreTarCommand { return &RestoreTarCommand{ - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + logDest: logdest, RetryPeriod: time.Second * 30, Pprof: "localhost:0", } @@ -76,7 +82,7 @@ func (cmd *RestoreTarCommand) Run(ctx context.Context) (err error) { var f io.Reader // read from Stdin if path specified as - if useStdin { - f = cmd.Stdin + f = os.Stdin } else { file, err := os.Open(cmd.Path) if err != nil { diff --git a/ctl/restore_tar_test.go b/ctl/restore_tar_test.go index efb8577b1..af07f736b 100644 --- a/ctl/restore_tar_test.go +++ b/ctl/restore_tar_test.go @@ -1,14 +1,15 @@ package ctl import ( - "bytes" "context" "errors" + "io" "net/http" "os" "strings" "testing" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/test" ) @@ -17,9 +18,8 @@ func TestRestoreTarCommand_Run(t *testing.T) { defer cluster.Close() cmd := cluster.GetNode(0) - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - cm := NewRestoreTarCommand(stdin, stdout, stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewRestoreTarCommand(cmLog) hostport := cmd.API.Node().URI.HostPort() cm.Host = hostport cm.Path = "" diff --git a/ctl/restore_test.go b/ctl/restore_test.go index 84cafdae9..015c83f27 100644 --- a/ctl/restore_test.go +++ b/ctl/restore_test.go @@ -4,12 +4,15 @@ package ctl import ( "context" "errors" - "os" + "io" "testing" + + "github.com/molecula/featurebase/v3/logger" ) func TestRestoreCommand_Run(t *testing.T) { - cm := NewRestoreCommand(os.Stdin, os.Stdout, os.Stderr) + cmLog := logger.NewStandardLogger(io.Discard) + cm := NewRestoreCommand(cmLog) cm.Path = "" err := cm.Run(context.Background()) if !errors.Is(err, UsageError) { diff --git a/ctl/server_test.go b/ctl/server_test.go index fc17e3050..b037c0e3d 100644 --- a/ctl/server_test.go +++ b/ctl/server_test.go @@ -2,7 +2,7 @@ package ctl import ( - "bytes" + "os" "testing" "github.com/molecula/featurebase/v3/server" @@ -11,9 +11,7 @@ import ( func TestBuildServerFlags(t *testing.T) { cm := &cobra.Command{} - buf := bytes.Buffer{} - stdin, stdout, stderr := GetIO(buf) - Server := server.NewCommand(stdin, stdout, stderr) + Server := server.NewCommand(os.Stderr) BuildServerFlags(cm, Server) if cm.Flags().Lookup("data-dir").Name == "" { t.Fatal("data-dir flag is required") diff --git a/ctl/testdata/rbf-check/err-invalid-page-type/data b/ctl/testdata/err-invalid-page-type/data similarity index 100% rename from ctl/testdata/rbf-check/err-invalid-page-type/data rename to ctl/testdata/err-invalid-page-type/data diff --git a/ctl/testdata/rbf-check/err-invalid-page-type/wal b/ctl/testdata/err-invalid-page-type/wal similarity index 100% rename from ctl/testdata/rbf-check/err-invalid-page-type/wal rename to ctl/testdata/err-invalid-page-type/wal diff --git a/ctl/testdata/rbf-check/ok/data b/ctl/testdata/ok/data similarity index 100% rename from ctl/testdata/rbf-check/ok/data rename to ctl/testdata/ok/data diff --git a/ctl/testdata/rbf-check/ok/wal b/ctl/testdata/ok/wal similarity index 100% rename from ctl/testdata/rbf-check/ok/wal rename to ctl/testdata/ok/wal diff --git a/ctl/testdata/rbf-pages/err-invalid-page-type/data b/ctl/testdata/rbf-pages/err-invalid-page-type/data deleted file mode 100644 index f088c25c9..000000000 Binary files a/ctl/testdata/rbf-pages/err-invalid-page-type/data and /dev/null differ diff --git a/ctl/testdata/rbf-pages/err-invalid-page-type/wal b/ctl/testdata/rbf-pages/err-invalid-page-type/wal deleted file mode 100644 index e69de29bb..000000000 diff --git a/ctl/testdata/rbf-pages/ok/data b/ctl/testdata/rbf-pages/ok/data deleted file mode 100644 index e4c3b621e..000000000 Binary files a/ctl/testdata/rbf-pages/ok/data and /dev/null differ diff --git a/ctl/testdata/rbf-pages/ok/wal b/ctl/testdata/rbf-pages/ok/wal deleted file mode 100644 index e69de29bb..000000000 diff --git a/dax/server/server.go b/dax/server/server.go index 7de324fb4..921eca9c3 100644 --- a/dax/server/server.go +++ b/dax/server/server.go @@ -53,7 +53,7 @@ type Command struct { done chan struct{} // Standard input/output - *featurebase.CmdIO + stderr io.Writer ln net.Listener listenURI *fbnet.URI @@ -94,11 +94,11 @@ func OptCommandConfig(config *Config) CommandOption { } // NewCommand returns a new instance of Command. -func NewCommand(stdin io.Reader, stdout, stderr io.Writer, opts ...CommandOption) *Command { +func NewCommand(stderr io.Writer, opts ...CommandOption) *Command { c := &Command{ Config: NewConfig(), - CmdIO: featurebase.NewCmdIO(stdin, stdout, stderr), + stderr: stderr, registerFns: make([]registerFn, 0), @@ -415,7 +415,7 @@ func (m *Command) setupServer() error { m.logger.Warnf("No snapshotter configured.") } - fbcmd := featurebaseserver.NewCommand(m.CmdIO.Stdin, m.CmdIO.Stdout, m.CmdIO.Stderr, + fbcmd := featurebaseserver.NewCommand(m.stderr, featurebaseserver.OptCommandSetConfig(&m.Config.Computer.Config), featurebaseserver.OptCommandServerOptions( featurebase.OptServerIsComputeNode(true), @@ -465,7 +465,7 @@ func (m *Command) setupLogger() error { var f *logger.FileWriter var err error if m.Config.LogPath == "" { - m.logOutput = m.Stderr + m.logOutput = os.Stderr } else { f, err = logger.NewFileWriter(m.Config.LogPath) if err != nil { diff --git a/executor_test.go b/executor_test.go index 573405774..3daa2fe07 100644 --- a/executor_test.go +++ b/executor_test.go @@ -26,6 +26,7 @@ import ( pilosa "github.com/molecula/featurebase/v3" "github.com/molecula/featurebase/v3/ctl" "github.com/molecula/featurebase/v3/disco" + "github.com/molecula/featurebase/v3/logger" "github.com/molecula/featurebase/v3/pql" "github.com/molecula/featurebase/v3/proto" "github.com/molecula/featurebase/v3/server" @@ -7398,8 +7399,8 @@ func backupTest(t *testing.T, c *test.Cluster, index string) { func chkSumCluster(t *testing.T, c *test.Cluster) string { buf := &bytes.Buffer{} - - chkSum := ctl.NewChkSumCommand(nil, buf, buf) + chkSumLog := logger.NewStandardLogger(buf) + chkSum := ctl.NewChkSumCommand(chkSumLog) chkSum.Host = c.Nodes[len(c.Nodes)-1].URL() if err := chkSum.Run(context.Background()); err != nil { t.Fatalf("running checksum: %v", err) @@ -7416,7 +7417,8 @@ func backupCluster(t *testing.T, c *test.Cluster, index string) (backupDir strin td = td + "/backupTest" buf := &bytes.Buffer{} - backupCommand := ctl.NewBackupCommand(nil, buf, buf) + backupLog := logger.NewStandardLogger(buf) + backupCommand := ctl.NewBackupCommand(backupLog) backupCommand.Host = c.Nodes[len(c.Nodes)-1].URL() // don't pick node 0 so we don't always get primary (better code coverage) backupCommand.Index = index backupCommand.OutputDir = td @@ -7430,8 +7432,8 @@ func backupCluster(t *testing.T, c *test.Cluster, index string) (backupDir strin func restoreCluster(t *testing.T, backupDir string, c *test.Cluster) { buf := &bytes.Buffer{} - - restore := ctl.NewRestoreCommand(nil, buf, buf) + restoreLog := logger.NewStandardLogger(buf) + restore := ctl.NewRestoreCommand(restoreLog) restore.Host = c.Nodes[len(c.Nodes)-1].URL() restore.Path = backupDir if err := restore.Run(context.Background()); err != nil { diff --git a/internal/clustertests/cluster_test.go b/internal/clustertests/cluster_test.go index 186f70cdd..8747e7798 100644 --- a/internal/clustertests/cluster_test.go +++ b/internal/clustertests/cluster_test.go @@ -2,7 +2,6 @@ package clustertest import ( - "bufio" "bytes" "context" "fmt" @@ -251,12 +250,8 @@ func TestClusterStuff(t *testing.T) { // replicas=3 and the backup command will retry on replicas. // featurebase backup cmd can't be used for a test expected to fail // because code coverage report won't be generated. - buf := bytes.Buffer{} - rder := []byte{} - stdin := bytes.NewReader(rder) - stdout := bufio.NewWriter(&buf) - stderr := bufio.NewWriter(&buf) - backup := ctl.NewBackupCommand(stdin, stdout, stderr) + backuplog := logger.NewStandardLogger(os.Stderr) + backup := ctl.NewBackupCommand(backuplog) backup.Host = "--host=pilosa1:10101" backup.OutputDir = tmpdir + "/backuptest2" backup.RetryPeriod = time.Millisecond * 200 diff --git a/server/server.go b/server/server.go index 676ad657d..ac3fff9e3 100644 --- a/server/server.go +++ b/server/server.go @@ -66,9 +66,6 @@ type Command struct { // Configuration. Config *Config - // Standard input/output - *pilosa.CmdIO - // Started will be closed once Command.Start is finished. Started chan struct{} // done will be closed when Command.Close() is called @@ -101,6 +98,11 @@ type Command struct { isComputeNode bool } +// Logger returns the command's associated Logger to maintain CommandWithTLSSupport interface compatibility +func (cmd *Command) Logger() logger.Logger { + return cmd.logger +} + type CommandOption func(c *Command) error func OptCommandServerOptions(opts ...pilosa.ServerOption) CommandOption { @@ -169,11 +171,11 @@ type Injections struct { } // NewCommand returns a new instance of Main. -func NewCommand(stdin io.Reader, stdout, stderr io.Writer, opts ...CommandOption) *Command { +func NewCommand(stderr io.Writer, opts ...CommandOption) *Command { c := &Command{ Config: NewConfig(), - CmdIO: pilosa.NewCmdIO(stdin, stdout, stderr), + logOutput: stderr, Started: make(chan struct{}), done: make(chan struct{}), @@ -730,9 +732,7 @@ func (m *Command) HTTPHandler() http.Handler { func (m *Command) setupLogger() error { var f *logger.FileWriter var err error - if m.Config.LogPath == "" { - m.logOutput = m.Stderr - } else { + if m.Config.LogPath != "" { f, err = logger.NewFileWriter(m.Config.LogPath) if err != nil { return errors.Wrap(err, "opening file") diff --git a/test/pilosa.go b/test/pilosa.go index def44e606..308b2f632 100644 --- a/test/pilosa.go +++ b/test/pilosa.go @@ -47,7 +47,11 @@ func newCommand(tb DirCleaner, opts ...server.CommandOption) *Command { }, opts...) m := &Command{commandOptions: opts} - m.Command = server.NewCommand(bytes.NewReader(nil), io.Discard, io.Discard, opts...) + output := io.Discard + if testing.Verbose() { + output = os.Stderr + } + m.Command = server.NewCommand(output, opts...) // pick etcd ports using a socket rather than a real port err := GetPortsGenConfigs(tb, []*Command{m}) if err != nil { @@ -67,11 +71,6 @@ func newCommand(tb DirCleaner, opts ...server.CommandOption) *Command { m.Config.Translation.MapSize = 140000 m.Config.WorkerPoolSize = 2 - if testing.Verbose() { - m.Command.Stdout = os.Stdout - m.Command.Stderr = os.Stderr - } - return m } @@ -109,7 +108,11 @@ func (m *Command) Reopen() error { // Create new main with the same config. config := m.Command.Config - m.Command = server.NewCommand(bytes.NewReader(nil), io.Discard, io.Discard, m.commandOptions...) + output := io.Discard + if testing.Verbose() { + output = os.Stderr + } + m.Command = server.NewCommand(output, m.commandOptions...) m.Command.Config = config // Run new program.