Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
orpheuslummis committed Apr 24, 2023
1 parent 3edcd29 commit ec46190
Show file tree
Hide file tree
Showing 12 changed files with 133 additions and 149 deletions.
8 changes: 4 additions & 4 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,10 @@ func NewDefraCommand(cfg *config.Config) DefraCommand {

func (defraCmd *DefraCommand) Execute(ctx context.Context) error {
// Silence cobra's default output to control usage and error display.
rootCmd.SilenceUsage = true
rootCmd.SilenceErrors = true
rootCmd.SetOut(os.Stdout)
cmd, err := rootCmd.ExecuteContextC(ctx)
defraCmd.RootCmd.SilenceUsage = true
defraCmd.RootCmd.SilenceErrors = true
defraCmd.RootCmd.SetOut(os.Stdout)
cmd, err := defraCmd.RootCmd.ExecuteContextC(ctx)
if err != nil {
// Intentional cancellation.
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
Expand Down
56 changes: 32 additions & 24 deletions cli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,37 @@ It covers three possible situations:
- root dir exists and doesn't contain a config file
- root dir exists and contains a config file
*/
var initCmd = &cobra.Command{
Use: "init",
Short: "Initialize DefraDB's root directory and configuration file",
Long: "Initialize a directory for configuration and data at the given path.",
// Load a default configuration, considering env. variables and CLI flags.
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
if err := cfg.LoadWithRootdir(false); err != nil {
return errors.Wrap("failed to load configuration", err)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if config.FolderExists(cfg.Rootdir) {
if cfg.ConfigFileExists() {
if reinitialize {
if err := cfg.DeleteConfigFile(); err != nil {
return err
}
if err := cfg.WriteConfigFile(); err != nil {
return err
func MakeInitCommand(cfg *config.Config) *cobra.Command {
var reinitialize bool
var cmd = &cobra.Command{
Use: "init",
Short: "Initialize DefraDB's root directory and configuration file",
Long: "Initialize a directory for configuration and data at the given path.",
// Load a default configuration, considering env. variables and CLI flags.
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
if err := cfg.LoadWithRootdir(false); err != nil {
return errors.Wrap("failed to load configuration", err)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if config.FolderExists(cfg.Rootdir) {
if cfg.ConfigFileExists() {
if reinitialize {
if err := cfg.DeleteConfigFile(); err != nil {
return err
}
if err := cfg.WriteConfigFile(); err != nil {
return err
}
} else {
log.FeedbackError(
cmd.Context(),
fmt.Sprintf(
"Configuration file already exists at %v. Consider using --reinitialize",
cfg.ConfigFilePath(),
),
)
}
} else {
if err := cfg.WriteConfigFile(); err != nil {
Expand All @@ -67,8 +78,5 @@ var initCmd = &cobra.Command{
"Reinitialize the configuration file",
)

cmd.Flags().StringVar(
&cfg.Rootdir, "rootdir", config.DefaultRootDir(),
"Directory for data and configuration to use",
)
return cmd
}
22 changes: 9 additions & 13 deletions cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,10 @@ Start a database node, issue a request to a local or remote node, and much more.
DefraDB is released under the BSL license, (c) 2022 Democratized Data Foundation.
See https://docs.source.network/BSL.txt for more information.
`,
// Runs on subcommands before their Run function, to handle configuration and top-level flags.
// Loads the rootDir containing the configuration file, otherwise warn about it and load a default configuration.
// This allows some subcommands (`init`, `start`) to override the PreRun to create a rootDir by default.
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
if cfg.ConfigFileExists() {
if err := cfg.LoadWithRootdir(true); err != nil {
return errors.Wrap("failed to load config", err)
}
// Runs on subcommands before their Run function, to handle configuration and top-level flags.
// Loads the rootDir containing the configuration file, otherwise warn about it and load a default configuration.
// This allows some subcommands (`init`, `start`) to override the PreRun to create a rootDir by default.
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
if cfg.ConfigFileExists() {
if err := cfg.LoadWithRootdir(true); err != nil {
return errors.Wrap("failed to load config", err)
Expand All @@ -53,12 +49,11 @@ See https://docs.source.network/BSL.txt for more information.
},
}

func init() {
rootCmd.PersistentFlags().StringVar(
&cfg.Rootdir, "rootdir", config.DefaultRootDir(),
"Directory for data and configuration to use",
cmd.PersistentFlags().String(
"rootdir", "",
"Directory for data and configuration to use (default: $HOME/.defradb)",
)
err := cfg.BindFlag("rootdircli", cmd.PersistentFlags().Lookup("rootdir"))
err := cfg.BindFlag(config.RootdirKey, cmd.PersistentFlags().Lookup("rootdir"))
if err != nil {
log.FeedbackFatalE(context.Background(), "Could not bind rootdir", err)
}
Expand Down Expand Up @@ -125,5 +120,6 @@ func init() {
if err != nil {
log.FeedbackFatalE(context.Background(), "Could not bind api.address", err)
}

return cmd
}
17 changes: 4 additions & 13 deletions cli/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,29 +50,20 @@ func MakeStartCommand(cfg *config.Config) *cobra.Command {
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
if cfg.ConfigFileExists() {
if err := cfg.LoadWithRootdir(true); err != nil {
return errors.Wrap("failed to load config", err)
return config.NewErrLoadingConfig(err)
}
} else {
if err := cfg.LoadWithRootdir(false); err != nil {
return errors.Wrap("failed to load config", err)
return config.NewErrLoadingConfig(err)
}
if config.FolderExists(cfg.Rootdir) {
if err := cfg.WriteConfigFile(); err != nil {
return err
}
log.FeedbackInfo(cmd.Context(), fmt.Sprintf("Configuration loaded from DefraDB directory %v", cfg.Rootdir))
} else {
if err := cfg.LoadWithRootdir(false); err != nil {
return config.NewErrLoadingConfig(err)
}
if config.FolderExists(cfg.Rootdir) {
if err := cfg.WriteConfigFile(); err != nil {
return err
}
} else {
if err := cfg.CreateRootDirAndConfigFile(); err != nil {
return err
}
if err := cfg.CreateRootDirAndConfigFile(); err != nil {
return err
}
}
}
Expand Down
100 changes: 46 additions & 54 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ var log = logging.MustNewLogger("defra.config")

const (
DefaultAPIEmail = "example@example.com"
RootdirKey = "rootdircli"
defraEnvPrefix = "DEFRA"
logLevelDebug = "debug"
logLevelInfo = "info"
logLevelError = "error"
logLevelFatal = "fatal"
rootdirKey = "rootdircli"
)

// Config is DefraDB's main configuration struct, embedding component-specific config structs.
Expand All @@ -86,39 +86,48 @@ type Config struct {
v *viper.Viper
}

// DefaultConfig returns the default configuration.
// DefaultConfig returns the default configuration (or panics).
func DefaultConfig() *Config {
return &Config{
cfg := &Config{
Datastore: defaultDatastoreConfig(),
API: defaultAPIConfig(),
Net: defaultNetConfig(),
Log: defaultLogConfig(),
Rootdir: DefaultRootDir(),
Rootdir: "",
v: viper.New(),
}

cfg.v.SetEnvPrefix(defraEnvPrefix)
cfg.v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))

cfg.v.SetConfigName(DefaultConfigFileName)
cfg.v.SetConfigType(configType)

// Load default values in viper.
b, err := cfg.toBytes()
if err != nil {
panic(err)
}
if err = cfg.v.ReadConfig(bytes.NewReader(b)); err != nil {
panic(NewErrReadingConfigFile(err))
}

return cfg
}

// LoadWithRootdir loads a Config with parameters from defaults, config file, environment variables, and CLI flags.
// It loads from config file when `fromFile` is true, otherwise it loads directly from a default configuration.
// Use on a Config struct already loaded with default values from DefaultConfig().
// To be executed once at the beginning of the program.
func (cfg *Config) LoadWithRootdir(withRootdir bool) error {
var err error

// Use default logging configuration here, so that
// we can log errors in a consistent way even in the case of early failure.
defaultLogCfg := defaultLogConfig()
if err := defaultLogCfg.load(); err != nil {
return err
}

if err := cfg.loadDefaultViper(); err != nil {
return err
}

// using absolute rootdir for robustness.
cfg.Rootdir, err = filepath.Abs(cfg.Rootdir)
if err != nil {
if err := cfg.loadRootDirFromFlagOrDefault(); err != nil {
return err
}

Expand Down Expand Up @@ -147,57 +156,31 @@ func (cfg *Config) LoadWithRootdir(withRootdir bool) error {
return nil
}

// LoadRootDir obtains the rootdir param from the CLI flag `--rootdir` and loads it into the Config.
func (cfg *Config) LoadRootdir() error {
rootDir, err := cfg.getRootdir()
if err != nil {
return err
}
if err := cfg.setRootdir(rootDir); err != nil {
return err
}
return nil
}
func (cfg *Config) loadRootDirFromFlagOrDefault() error {
if cfg.Rootdir == "" {
// Check CLI flag
rootdir := cfg.v.GetString(RootdirKey)
if rootdir != "" {
return cfg.setRootdir(rootdir)
}

func (cfg *Config) getRootdir() (string, error) {
rootdir := cfg.v.GetString(rootdirKey)
if rootdir == "" {
return DefaultRootDir(), nil
return cfg.setRootdir(DefaultRootDir())
}
return rootdir, nil

return nil
}

func (cfg *Config) setRootdir(rootdir string) error {
var err error
if rootdir == "" {
return NewErrInvalidRootDir(rootdir)
}
cfg.Rootdir = rootdir
cfg.v.AddConfigPath(cfg.Rootdir)
return nil
}

func (cfg *Config) loadDefaultViper() error {
// for our DEFRA_ env vars
cfg.v.SetEnvPrefix(defraEnvPrefix)
cfg.v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
// for now, only one type with a specific filename supported
cfg.v.SetConfigName(DefaultConfigFileName)
// To support a minimal configuration, we load and bind the default config first.
cfg.v.SetConfigType(configType)
/*
We load the default config into the viper instance, from a default template, so that viper
can detect the environment variables that are set. This is because viper only detects environment
variables that are present in the config file (`AutomaticEnv`). So we load the default config into viper,
and then overwrite it with the actual config file.
*/
defaultConfig := DefaultConfig()
defaultConfigBytes, err := defaultConfig.toBytes()
// using absolute rootdir for robustness.
cfg.Rootdir, err = filepath.Abs(rootdir)
if err != nil {
return err
}
if err = cfg.v.ReadConfig(bytes.NewReader(defaultConfigBytes)); err != nil {
return NewErrReadingConfigFile(err)
}
cfg.v.AddConfigPath(cfg.Rootdir)
return nil
}

Expand Down Expand Up @@ -742,7 +725,7 @@ func (cfg *Config) BindFlag(key string, flag *pflag.Flag) error {
return cfg.v.BindPFlag(key, flag)
}

// ToJSON serializes the config to a JSON string.
// ToJSON serializes the config to a JSON byte array.
func (c *Config) ToJSON() ([]byte, error) {
jsonbytes, err := json.Marshal(c)
if err != nil {
Expand All @@ -751,6 +734,15 @@ func (c *Config) ToJSON() ([]byte, error) {
return jsonbytes, nil
}

// String serializes the config to a JSON string.
func (c *Config) String() string {
jsonbytes, err := c.ToJSON()
if err != nil {
return fmt.Sprintf("failed to convert config to string: %s", err)
}
return string(jsonbytes)
}

func (c *Config) toBytes() ([]byte, error) {
var buffer bytes.Buffer
tmpl := template.New("configTemplate")
Expand Down
6 changes: 4 additions & 2 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,10 @@ func TestJSONSerialization(t *testing.T) {

assert.NoError(t, errUnmarshal)
assert.NoError(t, errSerialize)
for _, v := range m {
assert.NotEmpty(t, v)
for k, v := range m {
if k != "Rootdir" { // Rootdir is not serialized
assert.NotEmpty(t, v)
}
}
}

Expand Down
6 changes: 4 additions & 2 deletions config/configfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,13 @@ func TestReadConfigFileForDatastore(t *testing.T) {
tmpdir := t.TempDir()

cfg := DefaultConfig()
cfg.Rootdir = tmpdir
err := cfg.setRootdir(tmpdir)
assert.NoError(t, err)
cfg.Datastore.Store = "badger"
cfg.Datastore.Badger.Path = "dataPath"
cfg.Datastore.Badger.ValueLogFileSize = 512 * MiB

err := cfg.WriteConfigFile()
err = cfg.WriteConfigFile()
assert.NoError(t, err)

configPath := filepath.Join(tmpdir, DefaultConfigFileName)
Expand All @@ -116,6 +117,7 @@ func TestReadConfigFileForDatastore(t *testing.T) {
assert.Equal(t, filepath.Join(tmpdir, cfg.Datastore.Badger.Path), cfgFromFile.Datastore.Badger.Path)
assert.Equal(t, cfg.Datastore.Badger.ValueLogFileSize, cfgFromFile.Datastore.Badger.ValueLogFileSize)
}

func TestConfigFileExists(t *testing.T) {
cfg := DefaultConfig()
err := cfg.setRootdir(t.TempDir())
Expand Down
25 changes: 0 additions & 25 deletions tests/integration/cli/client_dump_test.go

This file was deleted.

Loading

0 comments on commit ec46190

Please sign in to comment.