Skip to content
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
32 changes: 25 additions & 7 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"

"github.com/pelletier/go-toml/v2"
"github.com/spf13/cobra"

"github.com/bandprotocol/falcon/falcon"
Expand All @@ -29,17 +30,25 @@ func configShowCmd(app *falcon.App) *cobra.Command {
cmd := &cobra.Command{
Use: "show",
Aliases: []string{"s", "list", "l"},
Short: "Display global configuration",
Short: "Prints current configuration",
Args: withUsage(cobra.NoArgs),
Example: strings.TrimSpace(fmt.Sprintf(`
$ %s config show --home %s
$ %s cfg s`, appName, defaultHome, appName)),
$ %s cfg list`, appName, defaultHome, appName)),
RunE: func(cmd *cobra.Command, args []string) error {
_ = app
if app.Config == nil {
return fmt.Errorf("config does not exist: %s", app.HomePath)
}

b, err := toml.Marshal(app.Config)
if err != nil {
return err
}

fmt.Fprintln(cmd.OutOrStdout(), string(b))
return nil
},
}

return cmd
}

Expand All @@ -54,10 +63,19 @@ func configInitCmd(app *falcon.App) *cobra.Command {
$ %s config init --home %s
$ %s cfg i`, appName, defaultHome, appName)),
RunE: func(cmd *cobra.Command, args []string) error {
_ = app
return nil
home, err := cmd.Flags().GetString(flagHome)
if err != nil {
return err
}

file, err := cmd.Flags().GetString(flagFile)
if err != nil {
return err
}

return app.InitConfigFile(home, file)
},
}

cmd.Flags().StringP(flagFile, "f", "", "fetch toml data from specified file")
return cmd
}
1 change: 1 addition & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ package cmd

const (
flagHome = "home"
flagFile = "file"
)
81 changes: 80 additions & 1 deletion falcon/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ package falcon

import (
"context"
"fmt"
"os"
"path"

"github.com/pelletier/go-toml/v2"
"github.com/spf13/viper"
"go.uber.org/zap"

Expand All @@ -11,6 +15,11 @@ import (
"github.com/bandprotocol/falcon/falcon/chains"
)

const (
configFolderName = "config"
configFileName = "config.toml"
)

// App is the main application struct.
type App struct {
Log *zap.Logger
Expand Down Expand Up @@ -57,11 +66,81 @@ func (a *App) InitLogger(configLogLevel string) error {

// loadConfigFile reads config file into a.Config if file is present.
func (a *App) LoadConfigFile(ctx context.Context) error {
cfgPath := path.Join(a.HomePath, configFolderName, configFileName)
if _, err := os.Stat(cfgPath); err != nil {
// don't return error if file doesn't exist
return nil
}

// read the config from config path
cfg, err := LoadConfig(cfgPath)
if err != nil {
return err
}

// save configuration
a.Config = cfg

return nil
}

// InitConfigFile initializes the configuration to the given path.
func (a *App) InitConfigFile(homePath string) error {
func (a *App) InitConfigFile(homePath string, customFilePath string) error {
cfgDir := path.Join(homePath, configFolderName)
cfgPath := path.Join(cfgDir, configFileName)

// check if the config file already exists
// https://stackoverflow.com/questions/12518876/how-to-check-if-a-file-exists-in-go
if _, err := os.Stat(cfgPath); err == nil {
return fmt.Errorf("config already exists: %s", cfgPath)
} else if !os.IsNotExist(err) {
return err
}

// Load config from given custom file path if exists
var cfg *Config
var err error
switch {
case customFilePath != "":
cfg, err = LoadConfig(customFilePath) // Initialize with CustomConfig if file is provided
if err != nil {
return fmt.Errorf("LoadConfig file %v error %v", customFilePath, err)
}
default:
cfg = DefaultConfig() // Initialize with DefaultConfig if no file is provided
}

// Marshal config object into bytes
b, err := toml.Marshal(cfg)
if err != nil {
return err
}

// Create the home folder if doesn't exist
if _, err := os.Stat(homePath); os.IsNotExist(err) {
if err = os.Mkdir(homePath, os.ModePerm); err != nil {
return err
}
}

// Create the config folder if doesn't exist
if _, err := os.Stat(cfgDir); os.IsNotExist(err) {
if err = os.Mkdir(cfgDir, os.ModePerm); err != nil {
return err
}
}

// Create the file and write the default config to the given location.
f, err := os.Create(cfgPath)
if err != nil {
return err
}
defer f.Close()

if _, err = f.Write(b); err != nil {
return err
}

return nil
}

Expand Down
98 changes: 98 additions & 0 deletions falcon/app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package falcon_test

import (
"os"
"path"
"testing"
"time"

"github.com/pelletier/go-toml/v2"
"github.com/stretchr/testify/require"

"github.com/bandprotocol/falcon/falcon"
"github.com/bandprotocol/falcon/falcon/band"
)

func TestInitConfig(t *testing.T) {
tmpDir := t.TempDir()
customCfgPath := ""

app := falcon.NewApp(nil, nil, tmpDir, false, nil)

err := app.InitConfigFile(tmpDir, customCfgPath)
require.NoError(t, err)

require.FileExists(t, path.Join(tmpDir, "config", "config.toml"))

// read the file
b, err := os.ReadFile(path.Join(tmpDir, "config", "config.toml"))
require.NoError(t, err)

// unmarshal data
actual := &falcon.Config{}
err = toml.Unmarshal(b, actual)
require.NoError(t, err)

expect := falcon.DefaultConfig()

require.Equal(t, *expect, *actual)
}

func TestInitExistingConfig(t *testing.T) {
tmpDir := t.TempDir()
customCfgPath := ""

app := falcon.NewApp(nil, nil, tmpDir, false, nil)

err := app.InitConfigFile(tmpDir, customCfgPath)
require.NoError(t, err)

// second time should fail
err = app.InitConfigFile(tmpDir, customCfgPath)
require.ErrorContains(t, err, "config already exists:")
}

func TestInitCustomConfig(t *testing.T) {
tmpDir := t.TempDir()
customCfgPath := path.Join(tmpDir, "custom.toml")

// Create custom config file
cfg := `
target_chains = []
checking_packet_interval = 60000000000

[bandchain]
rpc_endpoints = ['http://localhost:26659']
timeout = 50
`
// write file
err := os.WriteFile(customCfgPath, []byte(cfg), 0o600)
require.NoError(t, err)

app := falcon.NewApp(nil, nil, tmpDir, false, nil)

err = app.InitConfigFile(tmpDir, customCfgPath)
require.NoError(t, err)

require.FileExists(t, path.Join(tmpDir, "config", "config.toml"))

// read the file
b, err := os.ReadFile(path.Join(tmpDir, "config", "config.toml"))
require.NoError(t, err)

// unmarshal data
actual := falcon.Config{}
err = toml.Unmarshal(b, &actual)
require.NoError(t, err)

expect := falcon.Config{
BandChainConfig: band.Config{
RpcEndpoints: []string{"http://localhost:26659"},
Timeout: 50,
},
TargetChains: []falcon.TargetChainConfig{},
CheckingPacketInterval: time.Minute,
}

require.Equal(t, expect, actual)
}
31 changes: 31 additions & 0 deletions falcon/config.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package falcon

import (
"os"
"time"

"github.com/pelletier/go-toml/v2"

"github.com/bandprotocol/falcon/falcon/band"
"github.com/bandprotocol/falcon/falcon/chains"
)
Expand All @@ -20,3 +23,31 @@ type Config struct {
TargetChains []TargetChainConfig `toml:"target_chains"`
CheckingPacketInterval time.Duration `toml:"checking_packet_interval"`
}

// DefaultConfig returns the default configuration.
func DefaultConfig() *Config {
return &Config{
BandChainConfig: band.Config{
RpcEndpoints: []string{"http://localhost:26657"},
Timeout: 5,
},
TargetChains: []TargetChainConfig{},
CheckingPacketInterval: time.Minute,
}
}

// LoadConfig reads config file from given path and return config object
func LoadConfig(cfgPath string) (*Config, error) {
byt, err := os.ReadFile(cfgPath)
if err != nil {
return &Config{}, err
}

// unmarshall them with Config into struct
cfg := &Config{}
err = toml.Unmarshal(byt, cfg)
if err != nil {
return &Config{}, err
}
return cfg, nil
}
50 changes: 50 additions & 0 deletions falcon/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package falcon_test

import (
"path"
"testing"

"github.com/stretchr/testify/require"

"github.com/bandprotocol/falcon/falcon"
falcon_test "github.com/bandprotocol/falcon/falcon/falcontest"
)

func TestShowConfig(t *testing.T) {
sys := falcon_test.NewSystem(t)

res := sys.RunWithInput(t, "config", "init")
require.NoError(t, res.Err)

res = sys.RunWithInput(t, "config", "show")
require.NoError(t, res.Err)

actual := res.Stdout.String()
expect := "target_chains = []\nchecking_packet_interval = 60000000000\n\n[bandchain]\nrpc_endpoints = ['http://localhost:26657']\ntimeout = 5\n\n"

require.Equal(t, expect, actual)
}

func TestShowEmptyConfig(t *testing.T) {
sys := falcon_test.NewSystem(t)

res := sys.RunWithInput(t, "config", "show")
require.ErrorContains(t, res.Err, "config does not exist:")
}

func TestLoadConfig(t *testing.T) {
tmpDir := t.TempDir()
customConfigPath := ""
cfgPath := path.Join(tmpDir, "config", "config.toml")

app := falcon.NewApp(nil, nil, tmpDir, false, nil)

// Prepare config before test
err := app.InitConfigFile(tmpDir, customConfigPath)
require.NoError(t, err)

actual, err := falcon.LoadConfig(cfgPath)
require.NoError(t, err)
expect := falcon.DefaultConfig()
require.Equal(t, expect, actual)
}
Loading