Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve several kwild cmd issues with config parsing and error handling #1284

Merged
merged 1 commit into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/key/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ func GenCmd() *cobra.Command {
Long: "The `gen` command generates a private key for use by validators.",
Example: genExample,
Args: cobra.RangeArgs(0, 1),
// Override the root command's PersistentPreRunE, so that we don't
// try to read the config from a ~/.kwild directory
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return nil },
RunE: func(cmd *cobra.Command, args []string) error {
keyType := crypto.KeyTypeSecp256k1 // default with 0 args
if len(args) > 0 {
Expand Down
3 changes: 3 additions & 0 deletions app/key/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ func InfoCmd() *cobra.Command {
Long: infoLong,
Example: infoExample,
Args: cobra.MaximumNArgs(1),
// Override the root command's PersistentPreRunE, so that we don't
// try to read the config from a ~/.kwild directory
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return nil },
RunE: func(cmd *cobra.Command, args []string) error {
// if len(args) == 1, then the private key is passed as a hex string
// otherwise, it is passed as a file path
Expand Down
2 changes: 1 addition & 1 deletion app/node/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func StartCmd() *cobra.Command {
var dbOwner string
cmd := &cobra.Command{
Use: "start",
Short: "Start the node (default command if none given)",
Short: "Start the node",
Long: "The `start` command starts the Kwil DB blockchain node.",
DisableAutoGenTag: true,
SilenceUsage: true,
Expand Down
15 changes: 10 additions & 5 deletions app/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,23 @@ func RootCmd() *cobra.Command {
display.BindOutputFormatFlag(cmd) // --output/-o

// There is a virtual "node" command grouping, but no actual "node" command yet.
cmd.AddCommand(node.StartCmd())
cmd.AddCommand(node.PrintConfigCmd())
cmd.AddCommand(node.StartCmd()) // needs merged config
cmd.AddCommand(node.PrintConfigCmd()) // needs merged config

// This group of command uses the merged config for fallback admin listen
// addr if the --rpcserver flag is not set.
cmd.AddCommand(rpc.NewAdminCmd())
cmd.AddCommand(validator.NewValidatorsCmd())
cmd.AddCommand(params.NewConsensusCmd())
cmd.AddCommand(setup.SetupCmd())
cmd.AddCommand(whitelist.WhitelistCmd())
cmd.AddCommand(block.NewBlockExecCmd())
cmd.AddCommand(migration.NewMigrationCmd())

cmd.AddCommand(setup.SetupCmd()) // only kinda needs merged config for `setup reset`

cmd.AddCommand(key.KeyCmd())
cmd.AddCommand(snapshot.NewSnapshotCmd())
cmd.AddCommand(migration.NewMigrationCmd())
cmd.AddCommand(block.NewBlockExecCmd())

cmd.AddCommand(seed.SeedCmd())
cmd.AddCommand(utils.NewCmdUtils())
cmd.AddCommand(verCmd.NewVersionCmd())
Expand Down
1 change: 1 addition & 0 deletions app/seed/seed.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func SeedCmd() *cobra.Command {
Short: "Run a network seeder",
Long: "The `seed` command starts a peer seeder process to crawl and bootstrap the network. This does not use the kwild node config. It will bind to TCP port 6609, and store config and data in the specified directory.",
Args: cobra.NoArgs,

RunE: func(cmd *cobra.Command, args []string) error {
logger := log.New(log.WithWriter(os.Stdout), log.WithFormat(log.FormatUnstructured),
log.WithName("SEEDER"))
Expand Down
116 changes: 37 additions & 79 deletions app/setup/genesis-hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import (
"os"
"path/filepath"

"github.com/kwilteam/kwil-db/app/node/conf"
"github.com/kwilteam/kwil-db/app/shared/bind"
"github.com/kwilteam/kwil-db/app/shared/display"
"github.com/kwilteam/kwil-db/app/snapshot"
"github.com/kwilteam/kwil-db/config"
"github.com/kwilteam/kwil-db/node"
"github.com/kwilteam/kwil-db/node/pg"
"github.com/spf13/cobra"
)

Expand All @@ -41,16 +41,19 @@ kwild setup genesis-hash --snapshot "/path/to/snapshot.sql.gz" --genesis "~/.kwi
)

func GenesisHashCmd() *cobra.Command {
var genesisFile, rootDir, snapshotFile string
var genesisFile, snapshotFile string

cmd := &cobra.Command{
Use: "genesis-hash",
Short: "Compute genesis hash from existing PostgreSQL data, and optionally update genesis.json.",
Long: genesisHashLong,
Example: genesisHashExample,
Args: cobra.NoArgs,
// Override the root's PersistentPreRunE to bind only the config file,
// not the full node flag set.
PersistentPreRunE: bind.ChainPreRuns(conf.PreRunBindConfigFileStrict[config.Config]), // but not the flags
RunE: func(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("snapshot") && cmd.Flags().Changed("root-dir") {
if cmd.Flags().Changed("snapshot") && cmd.Flags().Changed(bind.RootFlagName) {
return display.PrintErr(cmd, errors.New("cannot use both --snapshot and --root-dir"))
}

Expand All @@ -61,43 +64,30 @@ func GenesisHashCmd() *cobra.Command {
if err != nil {
return display.PrintErr(cmd, err)
}
} else {
var pgConf *pg.ConnConfig
var err error
if rootDir != "" {
rootDir, err = node.ExpandPath(rootDir)
if err != nil {
return display.PrintErr(cmd, err)
}

pgConf, err = loadPGConfigFromTOML(cmd, rootDir)
if err != nil {
return display.PrintErr(cmd, err)
}
} else {
pgConf, err = bind.GetPostgresFlags(cmd)
if err != nil {
return display.PrintErr(cmd, err)
}
} else { // create a snapshot first
dbCfg := conf.ActiveConfig().DB
pgConf, err := bind.GetPostgresFlags(cmd, &dbCfg)
if err != nil {
return display.PrintErr(cmd, fmt.Errorf("failed to get postgres flags: %v", err))
}

dir, err := tmpKwilAdminSnapshotDir()
if err != nil {
return display.PrintErr(cmd, err)
}

// ensure the temp admin snapshots directory exists
err = ensureTmpKwilAdminDir(dir)
// clean up any previous temp admin snapshots
err = cleanupTmpKwilAdminDir(dir)
if err != nil {
return display.PrintErr(cmd, err)
}
defer cleanupTmpKwilAdminDir(dir) // clean up temp admin snapshots directory on exit after app hash computation

// clean up any previous temp admin snapshots
err = cleanupTmpKwilAdminDir(dir)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@charithabandi AFAICT, this was deleting the directory that was just created above, and the PGDump function was recreating it internally. Please double check this for me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, its temporary dir for storing snapshots till we get the genesis hash, we don't actually need it after.

Copy link
Member Author

@jchappelow jchappelow Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that's why we defer the cleanup, but I'm saying the original order in this function made no sense. It created it and immediately removed it, then created it again (this last one inside snapshot.PGDump).

// ensure the temp admin snapshots directory exists
err = ensureTmpKwilAdminDir(dir)
if err != nil {
return display.PrintErr(cmd, err)
}
defer cleanupTmpKwilAdminDir(dir) // clean up temp admin snapshots directory on exit after app hash computation

_, _, genCfg, err := snapshot.PGDump(cmd.Context(), pgConf.DBName, pgConf.User, pgConf.Pass, pgConf.Host, pgConf.Port, dir)
if err != nil {
Expand All @@ -107,15 +97,22 @@ func GenesisHashCmd() *cobra.Command {
appHash = genCfg.StateHash
}

return writeAndReturnGenesisHash(cmd, genesisFile, appHash)
if genesisFile != "" {
err := writeAndReturnGenesisHash(genesisFile, appHash)
if err != nil {
return display.PrintErr(cmd, err)
}
}
return display.PrintCmd(cmd, &genesisHashRes{
Hash: base64.StdEncoding.EncodeToString(appHash),
})
},
}

cmd.Flags().StringVarP(&genesisFile, "genesis", "g", "", "optional path to the genesis file to patch with the computed app hash")
cmd.Flags().StringVarP(&rootDir, "root-dir", "r", "", "optional path to the root directory of the kwild node from which the genesis hash will be computed")
Copy link
Member Author

@jchappelow jchappelow Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Root is bound globally. A handful of commands hide the flag, but the root command binds it for all subcommands.

Copy link
Collaborator

@brennanjl brennanjl Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have root dir bound globally?

Entire subcommand (e.g. validators) have no use for it.

Copy link
Member Author

@jchappelow jchappelow Jan 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost all commands actually do. In the case of all the commands that use admin RPC, like validators, the on-disk config is used as a default when --rpcserver is not set on the CLI (if the config file doesn't exist, the hard-coded default are used, of course). This means root directory can affect it.
We have the ability to hide it for other commands as needed though. For example key gen had it's own output flag, and the root flag their was confusing, so I hid it.

cmd.Flags().StringVarP(&snapshotFile, "snapshot", "s", "", "optional path to the snapshot file to use for the genesis hash computation")

bind.BindPostgresFlags(cmd)
bind.BindPostgresFlags(cmd, &conf.ActiveConfig().DB)
return cmd
}

Expand Down Expand Up @@ -152,56 +149,20 @@ func appHashFromSnapshotFile(filePath string) ([]byte, error) {
return hash.Sum(nil), nil
}

// loadPGConfigFromTOML loads the postgres connection configuration from the toml file
// and merges it with the flags set in the command.
func loadPGConfigFromTOML(cmd *cobra.Command, rootDir string) (*pg.ConnConfig, error) {
tomlFile := config.ConfigFilePath(rootDir)

// Check if the config file exists
if _, err := os.Stat(tomlFile); os.IsNotExist(err) {
return bind.GetPostgresFlags(cmd) // return the flags if the config file does not exist
}

cfg, err := config.LoadConfig(tomlFile)
func writeAndReturnGenesisHash(genesisFile string, appHash []byte) error {
genesisFile, err := node.ExpandPath(genesisFile)
if err != nil {
return nil, fmt.Errorf("failed to load config from toml file: %w", err)
return err
}

conf := &pg.ConnConfig{
Host: cfg.DB.Host,
Port: cfg.DB.Port,
User: cfg.DB.User,
Pass: cfg.DB.Pass,
DBName: cfg.DB.DBName,
cfg, err := config.LoadGenesisConfig(genesisFile)
if err != nil {
return err
}

// merge the flags with the config file
return bind.MergePostgresFlags(conf, cmd)
}

func writeAndReturnGenesisHash(cmd *cobra.Command, genesisFile string, appHash []byte) error {
if genesisFile != "" {
genesisFile, err := node.ExpandPath(genesisFile)
if err != nil {
return display.PrintErr(cmd, err)
}

cfg, err := config.LoadGenesisConfig(genesisFile)
if err != nil {
return display.PrintErr(cmd, err)
}

cfg.StateHash = appHash
cfg.StateHash = appHash

err = cfg.SaveAs(genesisFile)
if err != nil {
return display.PrintErr(cmd, err)
}
}

return display.PrintCmd(cmd, &genesisHashRes{
Hash: base64.StdEncoding.EncodeToString(appHash),
})
return cfg.SaveAs(genesisFile)
}

type genesisHashRes struct {
Expand Down Expand Up @@ -234,14 +195,11 @@ func tmpKwilAdminSnapshotDir() (string, error) {

// ensureTmpKwilAdminDir ensures that the temporary directory for kwil-admin snapshots exists.
func ensureTmpKwilAdminDir(dir string) error {
if _, err := os.Stat(dir); os.IsNotExist(err) {
_, err := os.Stat(dir)
if os.IsNotExist(err) {
err = os.Mkdir(dir, 0755)
if err != nil {
return err
}
}

return nil
return err
}

// cleanupTmpKwilAdminDir removes the temporary directory for kwil-admin snapshots.
Expand Down
3 changes: 3 additions & 0 deletions app/setup/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ func GenesisCmd() *cobra.Command {
DisableDefaultCmd: true,
},
Args: cobra.NoArgs,
// Override the root command's PersistentPreRunE, so that we don't
// try to read the config from a ~/.kwild directory.
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return nil },
RunE: func(cmd *cobra.Command, args []string) error {
outDir, err := node.ExpandPath(output)
if err != nil {
Expand Down
10 changes: 10 additions & 0 deletions app/setup/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ func InitCmd() *cobra.Command {
DisableDefaultCmd: true,
},
Args: cobra.NoArgs,
// Override parent persistent prerun so we don't try to read an existing
// config file; only the default+flags+env for generating a new root.
PersistentPreRunE: bind.ChainPreRuns(bind.MaybeEnableCLIDebug,
conf.PreRunBindFlags, conf.PreRunBindEnvMatching, conf.PreRunPrintEffectiveConfig),
RunE: func(cmd *cobra.Command, args []string) error {
genSnapshotFlag := cmd.Flag("genesis-snapshot").Changed
genStateFlag := cmd.Flag("genesis-state").Changed
Expand All @@ -75,6 +79,12 @@ func InitCmd() *cobra.Command {
return err
}

// Ensure the output directory does not already exist...
if _, err := os.Stat(outDir); err == nil {
return display.PrintErr(cmd, fmt.Errorf("output directory %s already exists", outDir))
}

// create the output directory
if err := os.MkdirAll(outDir, nodeDirPerm); err != nil {
return err
}
Expand Down
14 changes: 11 additions & 3 deletions app/setup/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import (
"os"
"path/filepath"

"github.com/kwilteam/kwil-db/app/custom"
"github.com/kwilteam/kwil-db/app/node/conf"
"github.com/kwilteam/kwil-db/app/shared/bind"
"github.com/kwilteam/kwil-db/app/shared/display"
"github.com/kwilteam/kwil-db/config"
"github.com/kwilteam/kwil-db/node"
"github.com/kwilteam/kwil-db/node/pg"

Expand All @@ -31,6 +35,9 @@ func ResetCmd() *cobra.Command {
Long: resetLong,
Example: resetExample,
Args: cobra.NoArgs,
// Override the root's PersistentPreRunE to bind only the config file,
// not the full node flag set.
PersistentPreRunE: bind.ChainPreRuns(conf.PreRunBindConfigFileStrict[config.Config]), // but not the flags
RunE: func(cmd *cobra.Command, args []string) error {
rootDir, err := bind.RootDir(cmd)
if err != nil {
Expand All @@ -45,9 +52,10 @@ func ResetCmd() *cobra.Command {
return fmt.Errorf("root directory %s does not exist", rootDir)
}

pgConf, err := loadPGConfigFromTOML(cmd, rootDir)
dbCfg := conf.ActiveConfig().DB
pgConf, err := bind.GetPostgresFlags(cmd, &dbCfg)
if err != nil {
return err
return display.PrintErr(cmd, fmt.Errorf("failed to get postgres flags: %v", err))
}

err = resetPGState(cmd.Context(), pgConf)
Expand Down Expand Up @@ -96,7 +104,7 @@ func ResetCmd() *cobra.Command {
}

cmd.Flags().BoolVar(&all, "all", false, "reset all data, if this is not set, only the app state will be reset")
bind.BindPostgresFlags(cmd)
bind.BindPostgresFlags(cmd, &custom.DefaultConfig().DB)

return cmd
}
Expand Down
4 changes: 4 additions & 0 deletions app/setup/testnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ func TestnetCmd() *cobra.Command {
var outDir, dbOwner string

cmd := &cobra.Command{

Use: "testnet",
Short: "Generate configuration for a new test network with multiple nodes",
Long: "The `testnet` command generates a configuration for a new test network with multiple nodes. " +
"For a configuration set that can be run on the same host, use the `--unique-ports` flag.",
// Override the root command's PersistentPreRunE, so that we don't
// try to read the config from a ~/.kwild directory
PersistentPreRunE: func(cmd *cobra.Command, args []string) error { return nil },
RunE: func(cmd *cobra.Command, args []string) error {
return GenerateTestnetConfigs(&TestnetConfig{
RootDir: outDir,
Expand Down
Loading
Loading