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

feat: overhaul lazy init in gnoland start #1985

Merged
merged 21 commits into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
14 changes: 13 additions & 1 deletion gno.land/cmd/gnoland/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package main
import (
"flag"
"fmt"
"path/filepath"
"reflect"
"strings"

"github.com/gnolang/gno/tm2/pkg/bft/config"
"github.com/gnolang/gno/tm2/pkg/commands"
)

Expand Down Expand Up @@ -39,11 +41,21 @@ func (c *configCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(
&c.configPath,
"config-path",
"./config.toml",
constructConfigPath(defaultNodeDir),
"the path for the config.toml",
)
}

// constructConfigPath constructs the default config path, using
// the given node directory
func constructConfigPath(nodeDir string) string {
return filepath.Join(
nodeDir,
config.DefaultConfigDir,
config.DefaultConfigFileName,
)
}

// getFieldAtPath fetches the given field from the given path
func getFieldAtPath(currentValue reflect.Value, path []string) (*reflect.Value, error) {
// Look at the current section, and figure out if
Expand Down
35 changes: 33 additions & 2 deletions gno.land/cmd/gnoland/config_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,27 @@
import (
"context"
"errors"
"flag"
"fmt"
"os"
"path/filepath"

"github.com/gnolang/gno/tm2/pkg/bft/config"
"github.com/gnolang/gno/tm2/pkg/commands"
osm "github.com/gnolang/gno/tm2/pkg/os"
)

var errInvalidConfigOutputPath = errors.New("invalid config output path provided")

type configInitCfg struct {
configCfg

forceOverwrite bool
}

// newConfigInitCmd creates the config init command
func newConfigInitCmd(io commands.IO) *commands.Command {
cfg := &configCfg{}
cfg := &configInitCfg{}

cmd := commands.NewCommand(
commands.Metadata{
Expand All @@ -32,15 +42,36 @@
return cmd
}

func execConfigInit(cfg *configCfg, io commands.IO) error {
func (c *configInitCfg) RegisterFlags(fs *flag.FlagSet) {
c.configCfg.RegisterFlags(fs)

fs.BoolVar(
&c.forceOverwrite,
"force",
false,
"overwrite existing config.toml, if any",
)
}

func execConfigInit(cfg *configInitCfg, io commands.IO) error {
// Check the config output path
if cfg.configPath == "" {
return errInvalidConfigOutputPath
}

// Make sure overwriting the config is enabled
if osm.FileExists(cfg.configPath) && !cfg.forceOverwrite {
return errOverwriteNotEnabled
}

// Get the default config
c := config.DefaultConfig()

// Make sure the path is created
if err := os.MkdirAll(filepath.Dir(cfg.configPath), 0o755); err != nil {
return fmt.Errorf("unable to create config dir, %w", err)

Check warning on line 72 in gno.land/cmd/gnoland/config_init.go

View check run for this annotation

Codecov / codecov/patch

gno.land/cmd/gnoland/config_init.go#L72

Added line #L72 was not covered by tests
}

// Save the config to the path
if err := config.WriteConfigFile(cfg.configPath, c); err != nil {
return fmt.Errorf("unable to initialize config, %w", err)
Expand Down
69 changes: 69 additions & 0 deletions gno.land/cmd/gnoland/config_init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,73 @@ func TestConfig_Init(t *testing.T) {
assert.NoError(t, cfg.ValidateBasic())
assert.Equal(t, cfg, config.DefaultConfig())
})

t.Run("unable to overwrite config", func(t *testing.T) {
t.Parallel()

// Create a temporary directory
tempDir := t.TempDir()
path := filepath.Join(tempDir, "config.toml")

// Create the command
cmd := newRootCmd(commands.NewTestIO())
args := []string{
"config",
"init",
"--config-path",
path,
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
require.NoError(t, cmdErr)

// Verify the config is valid
cfg, err := config.LoadConfigFile(path)
require.NoError(t, err)

assert.NoError(t, cfg.ValidateBasic())
assert.Equal(t, cfg, config.DefaultConfig())

// Try to initialize again, expecting failure
cmd = newRootCmd(commands.NewTestIO())

cmdErr = cmd.ParseAndRun(context.Background(), args)
assert.ErrorIs(t, cmdErr, errOverwriteNotEnabled)
})

t.Run("config overwritten", func(t *testing.T) {
t.Parallel()

// Create a temporary directory
tempDir := t.TempDir()
path := filepath.Join(tempDir, "config.toml")

// Create the command
cmd := newRootCmd(commands.NewTestIO())
args := []string{
"config",
"init",
"--force",
"--config-path",
path,
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
require.NoError(t, cmdErr)

// Verify the config is valid
cfg, err := config.LoadConfigFile(path)
require.NoError(t, err)

assert.NoError(t, cfg.ValidateBasic())
assert.Equal(t, cfg, config.DefaultConfig())

// Try to initialize again, expecting success
cmd = newRootCmd(commands.NewTestIO())

cmdErr = cmd.ParseAndRun(context.Background(), args)
assert.NoError(t, cmdErr)
})
}
84 changes: 84 additions & 0 deletions gno.land/cmd/gnoland/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package main

import (
"context"
"flag"
"fmt"
"path/filepath"

"github.com/gnolang/gno/tm2/pkg/commands"
)

type initCfg struct {
dataDir string
forceOverwrite bool
}

// newInitCmd creates the gnoland init command
func newInitCmd(io commands.IO) *commands.Command {
cfg := &initCfg{}

return commands.NewCommand(
commands.Metadata{
Name: "init",
ShortUsage: "init [flags]",
ShortHelp: "initializes the default node secrets / configuration",
moul marked this conversation as resolved.
Show resolved Hide resolved
LongHelp: "initializes the node directory containing the secrets and configuration files",
},
cfg,
func(_ context.Context, _ []string) error {
return execInit(cfg, io)
},
)
}

func (c *initCfg) RegisterFlags(fs *flag.FlagSet) {
fs.StringVar(
&c.dataDir,
"data-dir",
defaultNodeDir,
"the path to the node's data directory",
)

fs.BoolVar(
&c.forceOverwrite,
"force",
false,
"overwrite existing data, if any",
)
}

func execInit(cfg *initCfg, io commands.IO) error {
// Create the gnoland config options
config := &configInitCfg{
configCfg: configCfg{
configPath: constructConfigPath(cfg.dataDir),
},
forceOverwrite: cfg.forceOverwrite,
}

// Run gnoland config init
if err := execConfigInit(config, io); err != nil {
return fmt.Errorf("unable to initialize config, %w", err)
}

// Create the gnoland secrets options
secrets := &secretsInitCfg{
commonAllCfg: commonAllCfg{
dataDir: constructSecretsPath(cfg.dataDir),
},
forceOverwrite: cfg.forceOverwrite,
}

// Run gnoland secrets init
if err := execSecretsInit(secrets, []string{}, io); err != nil {
return fmt.Errorf("unable to initialize secrets, %w", err)

Check warning on line 75 in gno.land/cmd/gnoland/init.go

View check run for this annotation

Codecov / codecov/patch

gno.land/cmd/gnoland/init.go#L75

Added line #L75 was not covered by tests
}

io.Println()

io.Printfln("Successfully initialized default node config at %q", filepath.Dir(config.configPath))
io.Printfln("Successfully initialized default node secrets at %q", secrets.dataDir)

return nil
}
109 changes: 109 additions & 0 deletions gno.land/cmd/gnoland/init_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package main

import (
"context"
"path/filepath"
"testing"

"github.com/gnolang/gno/tm2/pkg/bft/config"
"github.com/gnolang/gno/tm2/pkg/commands"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestInit(t *testing.T) {
t.Parallel()

verifyDataCommon := func(nodeDir string) {
// Verify the config is valid
cfg, err := config.LoadConfigFile(constructConfigPath(nodeDir))
require.NoError(t, err)

assert.NoError(t, cfg.ValidateBasic())
assert.Equal(t, cfg, config.DefaultConfig())

// Verify the validator key is saved
verifyValidatorKey(t, filepath.Join(constructSecretsPath(nodeDir), defaultValidatorKeyName))

// Verify the last sign validator state is saved
verifyValidatorState(t, filepath.Join(constructSecretsPath(nodeDir), defaultValidatorStateName))

// Verify the node p2p key is saved
verifyNodeKey(t, filepath.Join(constructSecretsPath(nodeDir), defaultNodeKeyName))
}

t.Run("config and secrets initialized", func(t *testing.T) {
t.Parallel()

// Create a temporary directory
tempDir := t.TempDir()

// Create the command
cmd := newRootCmd(commands.NewTestIO())
args := []string{
"init",
"--data-dir",
tempDir,
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
require.NoError(t, cmdErr)

// Verify the generated data
verifyDataCommon(tempDir)
})

t.Run("config and secrets not overwritten", func(t *testing.T) {
t.Parallel()

// Create a temporary directory
tempDir := t.TempDir()

// Create the command
cmd := newRootCmd(commands.NewTestIO())
args := []string{
"init",
"--data-dir",
tempDir,
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
require.NoError(t, cmdErr)

// Verify the generated data
verifyDataCommon(tempDir)

// Try to run the command again, expecting failure
cmdErr = cmd.ParseAndRun(context.Background(), args)
assert.ErrorIs(t, cmdErr, errOverwriteNotEnabled)
})

t.Run("config and secrets overwritten", func(t *testing.T) {
t.Parallel()

// Create a temporary directory
tempDir := t.TempDir()

// Create the command
cmd := newRootCmd(commands.NewTestIO())
args := []string{
"init",
"--force",
"--data-dir",
tempDir,
}

// Run the command
cmdErr := cmd.ParseAndRun(context.Background(), args)
require.NoError(t, cmdErr)

// Verify the generated data
verifyDataCommon(tempDir)

// Try to run the command again, expecting success
cmdErr = cmd.ParseAndRun(context.Background(), args)
assert.NoError(t, cmdErr)
})
}
1 change: 1 addition & 0 deletions gno.land/cmd/gnoland/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func newRootCmd(io commands.IO) *commands.Command {
newSecretsCmd(io),
newConfigCmd(io),
newGenesisCmd(io),
newInitCmd(io),
)

return cmd
Expand Down
Loading
Loading