diff --git a/internal/app/kwild/app.go b/internal/app/kwild/app.go index f4fe8b82d..02f194bf2 100644 --- a/internal/app/kwild/app.go +++ b/internal/app/kwild/app.go @@ -20,7 +20,7 @@ var rootCmd = &cobra.Command{ } var kwildCfg = config.DefaultConfig() -var cfgFile string +var rootDir string func Execute() error { rootCmd.AddCommand( @@ -32,7 +32,7 @@ func Execute() error { } func init() { - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "kwild config file") + rootCmd.PersistentFlags().StringVar(&rootDir, "root_dir", "~/.kwild", "kwild root directory for config and data") rootCmd.PersistentPreRunE = extractKwildConfig } @@ -46,7 +46,13 @@ func extractKwildConfig(cmd *cobra.Command, args []string) error { } } - err := kwildCfg.LoadKwildConfig(cfgFile) + rootDir, err := config.ExpandPath(rootDir) + if err != nil { + fmt.Println("Error while getting absolute path for config file: ", err) + return err + } + + err = kwildCfg.LoadKwildConfig(rootDir) if err != nil { fmt.Println("Failed to load config: ", err) return err diff --git a/internal/app/kwild/cmd/server/root.go b/internal/app/kwild/cmd/server/root.go index 533ba7280..551d91514 100644 --- a/internal/app/kwild/cmd/server/root.go +++ b/internal/app/kwild/cmd/server/root.go @@ -24,10 +24,6 @@ func NewStartCmd(cfg *config.KwildConfig) *cobra.Command { return errors.New("private key is not set") } - if cfg.RootDir == "" { - return errors.New("kwild home directory not set") - } - signalChan := make(chan os.Signal, 1) signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM) ctx, cancel := context.WithCancel(cmd.Context()) @@ -52,8 +48,6 @@ func NewStartCmd(cfg *config.KwildConfig) *cobra.Command { func AddKwildFlags(cmd *cobra.Command, cfg *config.KwildConfig) { // General APP flags: - cmd.Flags().StringVar(&cfg.RootDir, "home_dir", cfg.RootDir, "Kwild home directory to store blockchain, kwildb and other data") - cmd.Flags().StringVar(&cfg.AppCfg.PrivateKey, "app.private_key", cfg.AppCfg.PrivateKey, "Kwild app's private key") cmd.Flags().StringVar(&cfg.AppCfg.GrpcListenAddress, "app.grpc_listen_addr", cfg.AppCfg.GrpcListenAddress, "Kwild app gRPC listen address") diff --git a/internal/app/kwild/cmd/utils/init.go b/internal/app/kwild/cmd/utils/init.go index aee4ee062..65226c360 100644 --- a/internal/app/kwild/cmd/utils/init.go +++ b/internal/app/kwild/cmd/utils/init.go @@ -1,9 +1,6 @@ package utils import ( - "os" - "path/filepath" - "github.com/kwilteam/kwil-db/internal/pkg/nodecfg" "github.com/spf13/cobra" ) @@ -18,13 +15,7 @@ func InitFilesCmd() *cobra.Command { RunE: initFiles, } - homeDir, err := os.UserHomeDir() - if err != nil { - panic(err) - } - - initFilesCmd.Flags().StringVarP(&initFlags.OutputDir, "output-dir", "o", - filepath.Join(homeDir, ".kwild"), + initFilesCmd.Flags().StringVarP(&initFlags.OutputDir, "output-dir", "o", "~/.kwild", "directory to store initialization data for the node") initFilesCmd.Flags().Int64VarP(&initFlags.InitialHeight, "initial-height", "i", 0, "initial height of the first block") diff --git a/internal/app/kwild/cmd/utils/reset.go b/internal/app/kwild/cmd/utils/reset.go index b5cc6eb9c..fe9b14259 100644 --- a/internal/app/kwild/cmd/utils/reset.go +++ b/internal/app/kwild/cmd/utils/reset.go @@ -1,8 +1,6 @@ package utils import ( - "fmt" - "github.com/spf13/cobra" "github.com/spf13/viper" @@ -24,14 +22,14 @@ func NewResetAllCmd() *cobra.Command { Aliases: []string{"unsafe_reset_all"}, Short: "(unsafe) Remove all the blockchain's data and WAL, reset this node's validator to genesis state, for testing purposes only", RunE: func(cmd *cobra.Command, args []string) (err error) { - cfgFile := viper.GetString("config") - if cfgFile == "" { - fmt.Println("No config file specified") - return fmt.Errorf("no config file specified") + rootDir := viper.GetString("root_dir") + rootDir, err = config.ExpandPath(rootDir) + if err != nil { + return err } cfg := config.DefaultConfig() - err = cfg.LoadKwildConfig(cfgFile) + err = cfg.LoadKwildConfig(rootDir) if err != nil { return err } @@ -51,14 +49,14 @@ func NewResetStateCmd() *cobra.Command { Aliases: []string{"reset_state"}, Short: "(unsafe) Remove all the data and WAL, for testing purposes only", RunE: func(cmd *cobra.Command, args []string) (err error) { - cfgFile := viper.GetString("config") - if cfgFile == "" { - fmt.Println("No config file specified") - return fmt.Errorf("no config file specified") + rootDir := viper.GetString("root_dir") + rootDir, err = config.ExpandPath(rootDir) + if err != nil { + return err } cfg := config.DefaultConfig() - err = cfg.LoadKwildConfig(cfgFile) + err = cfg.LoadKwildConfig(rootDir) if err != nil { return err } diff --git a/internal/app/kwild/config/config.go b/internal/app/kwild/config/config.go index cf68a3fe3..de60faa7a 100644 --- a/internal/app/kwild/config/config.go +++ b/internal/app/kwild/config/config.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "strings" cometCfg "github.com/cometbft/cometbft/config" "github.com/kwilteam/kwil-db/pkg/log" @@ -11,7 +12,7 @@ import ( ) type KwildConfig struct { - RootDir string `mapstructure:"home_dir"` + RootDir string AppCfg *AppConfig `mapstructure:"app"` ChainCfg *cometCfg.Config `mapstructure:"chain"` @@ -46,19 +47,27 @@ type SnapshotConfig struct { SnapshotDir string `mapstructure:"snapshot_dir"` } -func (cfg *KwildConfig) LoadKwildConfig(cfgFile string) error { - err := cfg.ParseConfig(cfgFile) +func (cfg *KwildConfig) LoadKwildConfig(rootDir string) error { + // Expand root dir path + rootDir, err := ExpandPath(rootDir) + if err != nil { + return fmt.Errorf("failed to expand rootdir path: %v", err) + } + cfg.RootDir = rootDir + + cfgFile := filepath.Join(rootDir, "config.toml") + err = cfg.ParseConfig(cfgFile) if err != nil { return fmt.Errorf("failed to parse config file: %v", err) } - rootDir := cfg.RootDir - cfg.ChainCfg.SetRoot(filepath.Join(rootDir, "abci")) + err = cfg.sanitizeCfgPaths() + if err != nil { + return fmt.Errorf("failed to sanitize config paths: %v", err) + } cfg.configureLogging() cfg.configureCerts() - cfg.AppCfg.SqliteFilePath = rootify(cfg.AppCfg.SqliteFilePath, rootDir) - cfg.AppCfg.SnapshotConfig.SnapshotDir = rootify(cfg.AppCfg.SnapshotConfig.SnapshotDir, rootDir) if err := cfg.ChainCfg.ValidateBasic(); err != nil { return fmt.Errorf("invalid chain configuration data: %v", err) @@ -147,3 +156,33 @@ func rootify(path, rootDir string) string { } return filepath.Join(rootDir, path) } + +func (cfg *KwildConfig) sanitizeCfgPaths() error { + rootDir := cfg.RootDir + cfg.AppCfg.SqliteFilePath = rootify(cfg.AppCfg.SqliteFilePath, rootDir) + cfg.AppCfg.SnapshotConfig.SnapshotDir = rootify(cfg.AppCfg.SnapshotConfig.SnapshotDir, rootDir) + + cfg.ChainCfg.SetRoot(filepath.Join(rootDir, "abci")) + return nil +} + +func ExpandPath(path string) (string, error) { + var expandedPath string + + if tail, cut := strings.CutPrefix(path, "~/"); cut { + // Expands ~ in the path + homeDir, err := os.UserHomeDir() + if err != nil { + return "", err + } + expandedPath = filepath.Join(homeDir, tail) + } else { + // Expands relative paths + absPath, err := filepath.Abs(path) + if err != nil { + return "", fmt.Errorf("failed to get absolute path of file: %v due to error: %v", path, err) + } + expandedPath = absPath + } + return expandedPath, nil +} diff --git a/internal/pkg/nodecfg/generate.go b/internal/pkg/nodecfg/generate.go index 202752c98..c64a831ac 100644 --- a/internal/pkg/nodecfg/generate.go +++ b/internal/pkg/nodecfg/generate.go @@ -48,32 +48,35 @@ type TestnetGenerateConfig struct { func GenerateNodeConfig(genCfg *NodeGenerateConfig) error { cfg := config.DefaultConfig() - cfg.RootDir = genCfg.OutputDir - err := initFilesWithConfig(cfg, 0) + rootDir, err := config.ExpandPath(genCfg.OutputDir) if err != nil { + fmt.Println("Error while getting absolute path for output directory: ", err) return err } - cfg.ChainCfg.RPC.ListenAddress = "tcp://0.0.0.0:26657" - - outputPath := genCfg.OutputDir - // this only works on linux - if strings.HasPrefix(genCfg.OutputDir, "~/") { - dirname, err := os.UserHomeDir() - if err != nil { - return err - } - outputPath = filepath.Join(dirname, genCfg.OutputDir[2:]) + cfg.RootDir = rootDir + err = initFilesWithConfig(cfg, 0) + if err != nil { + return err } - writeConfigFile(filepath.Join(outputPath, "config.toml"), cfg) + cfg.ChainCfg.RPC.ListenAddress = "tcp://0.0.0.0:26657" + + writeConfigFile(filepath.Join(rootDir, "config.toml"), cfg) - fmt.Println("Successfully initialized node directory: ", genCfg.OutputDir) + fmt.Println("Successfully initialized node directory: ", rootDir) return nil } func GenerateTestnetConfig(genCfg *TestnetGenerateConfig) error { + rootDir, err := config.ExpandPath(genCfg.OutputDir) + if err != nil { + fmt.Println("Error while getting absolute path for output directory: ", err) + return err + } + genCfg.OutputDir = rootDir + if len(genCfg.Hostnames) > 0 && len(genCfg.Hostnames) != (genCfg.NValidators+genCfg.NNonValidators) { return fmt.Errorf( "testnet needs precisely %d hostnames (number of validators plus nonValidators) if --hostname parameter is used", diff --git a/internal/pkg/nodecfg/toml.go b/internal/pkg/nodecfg/toml.go index 1b5a5491f..9d132dd9d 100644 --- a/internal/pkg/nodecfg/toml.go +++ b/internal/pkg/nodecfg/toml.go @@ -71,9 +71,18 @@ const defaultConfigTemplate = ` # Only the config.toml and genesis file are required to run the kwild node # The rest of the files & directories are created by the kwild node on startup +####################################################################### +### Logging Config Options ### +####################################################################### +[log] +# Output level for logging, default is "info". Other options are "debug", "error", "warn", "trace" +log_level = "{{ .Logging.LogLevel }}" -# Directory to store the kwild node's data (described above) -home_dir = "{{ .RootDir }}" +# Output paths for the logger, can be stdout or a file path +output_paths = {{arrayFormatter .Logging.OutputPaths }} + +# Output format: 'plain' or 'json' +log_format = "{{ .Logging.LogFormat }}" ####################################################################### ### App Config Options ### @@ -139,19 +148,6 @@ max_snapshots = {{ .AppCfg.SnapshotConfig.MaxSnapshots}} # The directory where the snapshots are stored. Can be absolute or relative to the kwild root directory snapshot_dir = "{{ .AppCfg.SnapshotConfig.SnapshotDir }}" -####################################################################### -### Logging Config Options ### -####################################################################### -[log] -# Output level for logging, default is "info". Other options are "debug", "error", "warn", "trace" -log_level = "{{ .Logging.LogLevel }}" - -# Output paths for the logger, can be stdout or a file path -output_paths = {{arrayFormatter .Logging.OutputPaths }} - -# Output format: 'plain' or 'json' -log_format = "{{ .Logging.LogFormat }}" - ####################################################################### ### Chain Main Base Config Options ### #######################################################################