From 6b5f004000d015bb8abd16d25ef19436bfbc5da7 Mon Sep 17 00:00:00 2001 From: Dzmitry Hil Date: Fri, 8 Dec 2023 15:36:01 +0300 Subject: [PATCH] Add relayer `init` CLI command. (#56) --- README.md | 14 ++++ relayer/cmd/cli/cli.go | 154 ++++++++++++++++++++++++++++++++++++ relayer/cmd/cli/cli_test.go | 37 +++++++++ relayer/cmd/main.go | 66 +--------------- relayer/runner/runner.go | 12 ++- 5 files changed, 218 insertions(+), 65 deletions(-) create mode 100644 relayer/cmd/cli/cli.go create mode 100644 relayer/cmd/cli/cli_test.go diff --git a/README.md b/README.md index 2acf884f..efaae8a4 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,20 @@ make build-relayer make build-relayer-docker ``` +## Init relayer + +### Init relayer default config + +The relayer uses `relayer.yaml` for its work. The file contains all required setting which can be adjusted. +To init the default config call. + +```bash +./coreumbridge-xrpl-relayer init +``` + +The command will generate the default `relayer.yaml` config in the `$HOME/.coreumbridge-xrpl-relayer`. +Optionally you can provide `--home` to set different home directory. + ## Run relayer in docker If relayer docker image is not built, build it. diff --git a/relayer/cmd/cli/cli.go b/relayer/cmd/cli/cli.go new file mode 100644 index 00000000..f5312a9e --- /dev/null +++ b/relayer/cmd/cli/cli.go @@ -0,0 +1,154 @@ +package cli + +import ( + "bufio" + "os" + "path" + "time" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/logger" + "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/runner" +) + +const ( + // DefaultHomeDir is default home for the relayer. + DefaultHomeDir = ".coreumbridge-xrpl-relayer" + // FlagHome is home flag. + FlagHome = "home" + // That key name is constant here temporary, we will take it from the relayer config later. + relayerKeyName = "coreumbridge-xrpl-relayer" +) + +// InitCmd returns the init cmd. +func InitCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "init", + Short: "Initializes the relayer home with the default config.", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + home, err := getRelayerHome(cmd) + if err != nil { + return err + } + log, err := getConsoleLogger() + if err != nil { + return err + } + log.Info(ctx, "Generating default settings", logger.StringField("home", home)) + if err = runner.InitConfig(home, runner.DefaultConfig()); err != nil { + return err + } + log.Info(ctx, "Settings are generated successfully") + return nil + }, + } + + addHomeFlag(cmd) + + return cmd +} + +// StartCmd returns the start cmd. +func StartCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "start", + Short: "Start relayer.", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + // scan helps to wait for any input infinitely and just then call the relayer. That handles + // the relayer restart in the container. Because after the restart the container is detached, relayer + // requests the keyring password and fail inanimately. + log, err := getConsoleLogger() + if err != nil { + return err + } + log.Info(ctx, "Press any key to start the relayer.") + input := bufio.NewScanner(os.Stdin) + input.Scan() + + // that code is just for an example and will be replaced later + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return errors.Wrap(err, "failed to get client context") + } + keyRecord, err := clientCtx.Keyring.Key(relayerKeyName) + if err != nil { + return errors.Wrap(err, "failed to get key from keyring") + } + address, err := keyRecord.GetAddress() + if err != nil { + return errors.Wrap(err, "failed to get address from the key record") + } + for { + select { + case <-ctx.Done(): + return nil + case <-time.After(time.Second): + log.Info(ctx, "Address from the keyring extracted.", logger.StringField("address", address.String())) + } + } + }, + } + addKeyringFlags(cmd) + + return cmd +} + +// KeyringCmd returns cosmos keyring cmd inti with the correct keys home. +func KeyringCmd() *cobra.Command { + return keys.Commands(path.Join(DefaultHomeDir, "keys")) +} + +func getRelayerHome(cmd *cobra.Command) (string, error) { + home, err := cmd.Flags().GetString(FlagHome) + if err != nil { + return "", errors.WithStack(err) + } + if home == "" || home == DefaultHomeDir { + home, err = getUserHomeDir(DefaultHomeDir) + if err != nil { + return "", err + } + } + + return home, nil +} + +func getUserHomeDir(subPath ...string) (string, error) { + dirname, err := os.UserHomeDir() + if err != nil { + return "", errors.Wrap(err, "failed to get user home dir") + } + for _, item := range subPath { + dirname = path.Join(dirname, item) + } + + return dirname, nil +} + +func addHomeFlag(cmd *cobra.Command) { + cmd.PersistentFlags().String(FlagHome, DefaultHomeDir, "Relayer home directory") +} + +func addKeyringFlags(cmd *cobra.Command) { + cmd.PersistentFlags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") + cmd.PersistentFlags().String(flags.FlagKeyringDir, path.Join(DefaultHomeDir, "keys"), "The client Keyring directory; if omitted, the default 'home' directory will be used") +} + +// returns the console logger initialised with the default logger config but with set `console` format. +func getConsoleLogger() (*logger.ZapLogger, error) { + cfg := runner.DefaultConfig().LoggingConfig + cfg.Format = "console" + zapLogger, err := logger.NewZapLogger(logger.ZapLoggerConfig(cfg)) + if err != nil { + return nil, err + } + + return zapLogger, nil +} diff --git a/relayer/cmd/cli/cli_test.go b/relayer/cmd/cli/cli_test.go new file mode 100644 index 00000000..6dd1c7f0 --- /dev/null +++ b/relayer/cmd/cli/cli_test.go @@ -0,0 +1,37 @@ +package cli_test + +import ( + "context" + "fmt" + "path" + "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + + "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/cmd/cli" + "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/runner" +) + +func TestInitCmd(t *testing.T) { + configPath := path.Join(t.TempDir(), "config-path") + configFilePath := path.Join(configPath, runner.ConfigFileName) + require.NoFileExists(t, configFilePath) + + args := []string{ + fmt.Sprintf("--%s=%s", cli.FlagHome, configPath), + } + executeCmd(t, cli.InitCmd(), args...) + require.FileExists(t, configFilePath) +} + +func executeCmd(t *testing.T, cmd *cobra.Command, args ...string) { + t.Helper() + + cmd.SetArgs(args) + if err := cmd.ExecuteContext(context.Background()); err != nil { + require.NoError(t, err) + } + + t.Logf("Command %s is executed successfully", cmd.Name()) +} diff --git a/relayer/cmd/main.go b/relayer/cmd/main.go index 1c928502..6a51d655 100644 --- a/relayer/cmd/main.go +++ b/relayer/cmd/main.go @@ -1,29 +1,17 @@ package main import ( - "bufio" "context" - "fmt" "os" - "path" - "time" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/cosmos/cosmos-sdk/client/keys" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/CoreumFoundation/coreum-tools/pkg/run" coreumapp "github.com/CoreumFoundation/coreum/v3/app" "github.com/CoreumFoundation/coreum/v3/pkg/config" -) - -var defaultKeyringDir = path.Join(".coreumbridge-xrpl-relayer", "keys") - -const ( - // That key name is constant here temporary, we will take it from the relayer config later. - relayerKeyName = "coreumbridge-xrpl-relayer" + "github.com/CoreumFoundation/coreumbridge-xrpl/relayer/cmd/cli" ) func main() { @@ -52,55 +40,9 @@ func RootCmd(ctx context.Context) *cobra.Command { } cmd.SetContext(ctx) - cmd.AddCommand(StartCmd(ctx)) - cmd.AddCommand(keys.Commands(defaultKeyringDir)) - - return cmd -} - -// StartCmd returns the start cmd. -func StartCmd(ctx context.Context) *cobra.Command { - cmd := &cobra.Command{ - Use: "start", - Short: "Start relayer.", - RunE: func(cmd *cobra.Command, args []string) error { - // scan helps to wait for any input infinitely and just then call the relayer. That handles - // the relayer restart in the container. Because after the restart the container is detached, relayer - // requests the keyring password and fail inanimately. - // TODO(dzmitryhil) replace to logger once we integrate the runner - fmt.Print("Press any key to start the relayer.") - input := bufio.NewScanner(os.Stdin) - input.Scan() - - // that code is just for an example and will be replaced later - clientCtx, err := client.GetClientQueryContext(cmd) - if err != nil { - return errors.Wrap(err, "failed to get client context") - } - keyRecord, err := clientCtx.Keyring.Key(relayerKeyName) - if err != nil { - return errors.Wrap(err, "failed to get key from keyring") - } - address, err := keyRecord.GetAddress() - if err != nil { - return errors.Wrap(err, "failed to get address from the key record") - } - for { - select { - case <-ctx.Done(): - return nil - case <-time.After(time.Second): - fmt.Printf("Address from the keyring:%s\n", address.String()) - } - } - }, - } - addKeyringFlags(cmd) + cmd.AddCommand(cli.InitCmd()) + cmd.AddCommand(cli.StartCmd()) + cmd.AddCommand(cli.KeyringCmd()) return cmd } - -func addKeyringFlags(cmd *cobra.Command) { - cmd.PersistentFlags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)") - cmd.PersistentFlags().String(flags.FlagKeyringDir, defaultKeyringDir, "The client Keyring directory; if omitted, the default 'home' directory will be used") -} diff --git a/relayer/runner/runner.go b/relayer/runner/runner.go index dca3666d..28281a52 100644 --- a/relayer/runner/runner.go +++ b/relayer/runner/runner.go @@ -33,8 +33,9 @@ import ( ) const ( - configVersion = "v1" - configFileName = "relayer.yaml" + configVersion = "v1" + // ConfigFileName is file name used for the relayer config. + ConfigFileName = "relayer.yaml" defaultCoreumChainID = coreumchainconstant.ChainIDMain ) @@ -360,6 +361,11 @@ func InitConfig(homePath string, cfg Config) error { return errors.Errorf("failed to initi config, file already exists, path:%s", path) } + err := os.MkdirAll(homePath, 0o700) + if err != nil { + return errors.Errorf("failed to create dirs by path:%s", path) + } + file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o600) if err != nil { return errors.Wrapf(err, "failed to create config file, path:%s", path) @@ -398,7 +404,7 @@ func ReadConfig(homePath string) (Config, error) { } func buildFilePath(homePath string) string { - return filepath.Join(homePath, configFileName) + return filepath.Join(homePath, ConfigFileName) } func getGRPCClientConn(grpcURL string) (*grpc.ClientConn, error) {