FB-1766: cleaning up the CmdIO objects passing alternate stdin/

stdout/stderr around

A lot of functions in the cmd and ctl packages were passing these
around and barely using them. Replaced them with a logger for most
functions. Some functions get an io.Writer instead so that their
tests can find the output they're looking for.

More cleanup on fb-1766: reworked the tests that were using io.Pipe
or os.Pipe to check their results so they now use a bytes.Buffer.

Unexported some variables that didn't need to be exported.
Fixed NewConfigCommand to use the provided stderr, not os.Stderr.
Added tests for rbf_dump, rbf_page, and keygen, since those weren't
being tested at all.

Added chksum_test, final cleanup.
This commit is contained in:
Lory Cloutier 2022-11-22 12:25:11 -06:00 committed by Lory Cloutier
parent a98b9144cb
commit f627199acb
63 changed files with 553 additions and 417 deletions

31
cmd.go
View file

@ -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
}

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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.",

View file

@ -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",

View file

@ -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.",

View file

@ -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)

View file

@ -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.",

View file

@ -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.",

View file

@ -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.",

View file

@ -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)",

View file

@ -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",

View file

@ -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",

View file

@ -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

View file

@ -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()

View file

@ -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.",

View file

@ -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,
}
}

View file

@ -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,

View file

@ -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 {

View file

@ -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 {

View file

@ -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) {

View file

@ -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

31
ctl/chksum_test.go Normal file
View file

@ -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.
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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())
}

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View file

@ -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())
}

View file

@ -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

View file

@ -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)

View file

@ -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
}

26
ctl/keygen_test.go Normal file
View file

@ -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())
}
}

View file

@ -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
}

View file

@ -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)
}
})

View file

@ -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

45
ctl/rbf_dump_test.go Normal file
View file

@ -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)
}
})
}

View file

@ -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")
}

44
ctl/rbf_page_test.go Normal file
View file

@ -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)
}
})
}

View file

@ -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)
}
}

View file

@ -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 [<nil>]
`[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)
}
})

View file

@ -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",

View file

@ -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 {

View file

@ -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 = ""

View file

@ -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) {

View file

@ -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")

Binary file not shown.

Binary file not shown.

View file

View file

@ -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 {

View file

@ -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 {

View file

@ -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

View file

@ -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")

View file

@ -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.