From 0dfa3de6fa31548af32464f789dd2fdc921e488f Mon Sep 17 00:00:00 2001 From: huy Date: Fri, 10 Jan 2025 15:07:12 -0800 Subject: [PATCH] Implement hd commands and app.go --- adapter/app/app.go | 65 ++++++++++++ adapter/hd-module/commands.go | 121 +++++++++++++++++++++++ adapter/hd-module/get-config-metadata.go | 46 +++++++++ adapter/hd-module/get-containers.go | 42 ++++++++ adapter/hd-module/get-log-file.go | 57 +++++++++++ adapter/hd-module/process-config.go | 70 +++++++++++++ adapter/hd-module/run.go | 54 ++++++++++ adapter/hd-module/set-config.go | 48 +++++++++ adapter/hd-module/version.go | 33 +++++++ adapter/utils/consts.go | 15 +++ adapter/utils/flags.go | 12 +++ go.mod | 1 + go.sum | 2 + shared/consts.go | 12 +++ 14 files changed, 578 insertions(+) create mode 100644 adapter/app/app.go create mode 100644 adapter/hd-module/commands.go create mode 100644 adapter/hd-module/get-config-metadata.go create mode 100644 adapter/hd-module/get-containers.go create mode 100644 adapter/hd-module/get-log-file.go create mode 100644 adapter/hd-module/process-config.go create mode 100644 adapter/hd-module/run.go create mode 100644 adapter/hd-module/set-config.go create mode 100644 adapter/hd-module/version.go create mode 100644 adapter/utils/consts.go create mode 100644 shared/consts.go diff --git a/adapter/app/app.go b/adapter/app/app.go new file mode 100644 index 0000000..1b34645 --- /dev/null +++ b/adapter/app/app.go @@ -0,0 +1,65 @@ +package app + +import ( + "fmt" + "os" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/shared" + "github.com/urfave/cli/v2" +) + +func CreateApp() *cli.App { + // Initialize application + app := cli.NewApp() + + // Set application info + app.Name = "Hyperdrive Stakewise Adapter" + app.Usage = "Adapter for the Hyperdrive Stakewise module" + app.Version = shared.StakewiseVersion + app.Authors = []*cli.Author{ + { + Name: "Nodeset", + Email: "info@nodeset.io", + }, + } + app.Copyright = "(c) 2024 NodeSet LLC" + + // Enable Bash Completion + app.EnableBashCompletion = true + + // Set application flags + app.Flags = []cli.Flag{ + utils.ConfigDirFlag, + utils.LogDirFlag, + utils.KeyFileFlag, + } + + // Register commands + config.RegisterCommands(app) + + app.Before = func(c *cli.Context) error { + // Make the authenticator + auth, err := utils.NewAuthenticator(c) + if err != nil { + return err + } + c.App.Metadata[utils.AuthenticatorMetadataKey] = auth + + return nil + } + app.BashComplete = func(c *cli.Context) { + // Load the context and flags prior to autocomplete + err := app.Before(c) + if err != nil { + fmt.Fprint(os.Stderr, err.Error()) + os.Exit(1) + } + + // Run the default autocomplete + cli.DefaultAppComplete(c) + } + + return app +} diff --git a/adapter/hd-module/commands.go b/adapter/hd-module/commands.go new file mode 100644 index 0000000..457a24f --- /dev/null +++ b/adapter/hd-module/commands.go @@ -0,0 +1,121 @@ +package hdmodule + +import ( + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/urfave/cli/v2" +) + +// Handles `hd-module` commands +func RegisterCommands(app *cli.App) { + app.Commands = append(app.Commands, &cli.Command{ + Name: "hd-module", + Aliases: []string{"hd"}, + Usage: "Handle Hyperdrive module commands", + Subcommands: []*cli.Command{ + { + Name: "version", + Aliases: []string{"v"}, + Flags: []cli.Flag{}, + Usage: "Print the module version.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return version() + }, + }, + { + Name: "get-log-file", + Aliases: []string{"l"}, + Flags: []cli.Flag{}, + Usage: "Get the path to a log file.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return getLogFile(c) + }, + }, + { + Name: "get-config-metadata", + Aliases: []string{"c"}, + Flags: []cli.Flag{}, + Usage: "Get the metadata for the module's configuration, representing how to present the parameters to the user.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return getConfigMetadata(c) + }, + }, + { + Name: "get-config-instance", + Aliases: []string{"i"}, + Flags: []cli.Flag{}, + Usage: "Get the instance of the module's configuration, with the values all set.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return getConfigInstance(c) + }, + }, + { + Name: "process-config", + Aliases: []string{"p"}, + Flags: []cli.Flag{}, + Usage: "Process the module's configuration, validating it without saving.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return processConfig(c) + }, + }, + { + Name: "set-config", + Aliases: []string{"s"}, + Flags: []cli.Flag{}, + Usage: "Sets the module's configuration, saving it to disk.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return setConfig(c) + }, + }, + { + Name: "get-containers", + Aliases: []string{"t"}, + Flags: []cli.Flag{}, + Usage: "Get the list of containers owned by this module.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return getContainers(c) + }, + }, + { + Name: "run", + Aliases: []string{"r"}, + Flags: []cli.Flag{}, + Usage: "Run a command.", + Action: func(c *cli.Context) error { + // Validate args + utils.ValidateArgCount(c, 0) + + // Run + return run(c) + }, + }, + }, + }) +} diff --git a/adapter/hd-module/get-config-metadata.go b/adapter/hd-module/get-config-metadata.go new file mode 100644 index 0000000..9bc7f84 --- /dev/null +++ b/adapter/hd-module/get-config-metadata.go @@ -0,0 +1,46 @@ +package hdmodule + +import ( + //"encoding/json" + "fmt" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + hdconfig "github.com/nodeset-org/hyperdrive/modules/config" + "github.com/urfave/cli/v2" +) + +func getConfigMetadata(c *cli.Context) error { + // Get the request + _, err := utils.HandleKeyedRequest[*utils.KeyedRequest](c) + if err != nil { + return err + } + + // Get the config + cfgMgr, err := config.NewAdapterConfigManager(c) + if err != nil { + return fmt.Errorf("error creating config manager: %w", err) + } + cfg, err := cfgMgr.LoadConfigFromDisk() + if err != nil { + return fmt.Errorf("error loading config: %w", err) + } + + // Handle no config file by using the default + if cfg == nil { + cfg = config.NewExampleConfig() + } + + // Create the response + cfgMap := hdconfig.MarshalConfigurationMetadataToMap(cfg) + bytes, err := json.Marshal(cfgMap) + if err != nil { + return fmt.Errorf("error marshalling config: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/hd-module/get-containers.go b/adapter/hd-module/get-containers.go new file mode 100644 index 0000000..bf6c100 --- /dev/null +++ b/adapter/hd-module/get-containers.go @@ -0,0 +1,42 @@ +package hdmodule + +import ( + "fmt" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/shared" + "github.com/urfave/cli/v2" +) + +// Response format for `get-containers` +type getContainersResponse struct { + // The list of containers owned by this module + Containers []string `json:"containers"` +} + +// Handle the `get-containers` command +func getContainers(c *cli.Context) error { + // Get the request + _, err := utils.HandleKeyedRequest[*utils.KeyedRequest](c) + if err != nil { + return err + } + + // Create the response + response := getContainersResponse{ + Containers: []string{ + shared.ServiceContainerName, + }, + } + + // Marshal it + bytes, err := json.Marshal(response) + if err != nil { + return fmt.Errorf("error marshalling get-containers response: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/hd-module/get-log-file.go b/adapter/hd-module/get-log-file.go new file mode 100644 index 0000000..0e776d2 --- /dev/null +++ b/adapter/hd-module/get-log-file.go @@ -0,0 +1,57 @@ +package hdmodule + +import ( + "fmt" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/nodeset-org/hyperdrive-stakewise/shared" + "github.com/urfave/cli/v2" +) + +// Request format for `get-log-file` +type getLogFileRequest struct { + utils.KeyedRequest + + // The log file source to retrieve + Source string `json:"source"` +} + +// Response format for `get-log-file` +type getLogFileResponse struct { + // The path to the log file + Path string `json:"path"` +} + +// Handle the `get-log-file` command +func getLogFile(c *cli.Context) error { + // Get the request + request, err := utils.HandleKeyedRequest[*getLogFileRequest](c) + if err != nil { + return err + } + + // Get the path + path := "" + switch request.Source { + case "adapter": + path = utils.AdapterLogFile + case shared.ServiceContainerName: + path = shared.ServiceLogFile + } + + // Create the response + response := getLogFileResponse{ + Path: path, + } + + // Marshal it + bytes, err := json.Marshal(response) + if err != nil { + return fmt.Errorf("error marshalling get-log-file response: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/hd-module/process-config.go b/adapter/hd-module/process-config.go new file mode 100644 index 0000000..7288620 --- /dev/null +++ b/adapter/hd-module/process-config.go @@ -0,0 +1,70 @@ +package hdmodule + +import ( + "fmt" + + "github.com/goccy/go-json" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config/ids" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + hdconfig "github.com/nodeset-org/hyperdrive/modules/config" + "github.com/urfave/cli/v2" +) + +// Request format for `process-config` +type processConfigRequest struct { + utils.KeyedRequest + + // The config instance to process + Config map[string]any `json:"config"` +} + +// Response format for `process-config` +type processConfigResponse struct { + // A list of errors that occurred during processing, if any + Errors []string `json:"errors"` + + // A list of ports that will be exposed + Ports map[string]uint16 `json:"ports"` +} + +// Handle the `process-config` command +func processConfig(c *cli.Context) error { + // Get the request + request, err := utils.HandleKeyedRequest[*processConfigRequest](c) + if err != nil { + return err + } + + // Get the config + cfg := config.NewExampleConfig() + err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) + if err != nil { + return err + } + + // This is where any examples of validation will go when added + errors := []string{} + + // Get the open ports + ports := map[string]uint16{} + if cfg.ServerConfig.PortMode.Value != config.PortMode_Closed { + ports[ids.ServerConfigID+"/"+ids.PortModeID] = uint16(cfg.ServerConfig.Port.Value) + } + + // Create the response + response := processConfigResponse{ + Errors: errors, + Ports: ports, + } + + // Marshal it + bytes, err := json.Marshal(response) + if err != nil { + return fmt.Errorf("error marshalling process-config response: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/hd-module/run.go b/adapter/hd-module/run.go new file mode 100644 index 0000000..8ce5956 --- /dev/null +++ b/adapter/hd-module/run.go @@ -0,0 +1,54 @@ +package hdmodule + +import ( + "fmt" + "os" + "strings" + + "github.com/kballard/go-shellquote" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/app" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + "github.com/urfave/cli/v2" +) + +// Request format for `run` +type runRequest struct { + utils.KeyedRequest + + // The command to run + Command string `json:"command"` +} + +// Handle the `run` command +func run(c *cli.Context) error { + // Get the request + request, err := utils.HandleKeyedRequest[*runRequest](c) + if err != nil { + return err + } + + // Prevent recursive calls + if strings.HasPrefix(request.Command, "hd-module") || strings.HasPrefix(request.Command, "hd") { + return fmt.Errorf("recursive calls to `run` are not allowed") + } + + //TODO: fix whatever's breaking the key flag in the sub command (do you need to make a new app?) + // Run the app with the new command + args, err := shellquote.Split(request.Command) + args = append([]string{ + os.Args[0], // Adapter path + fmt.Sprintf("--%s", utils.KeyFileFlag.Name), + c.String(utils.KeyFileFlag.Name), + fmt.Sprintf("--%s", utils.ConfigDirFlag.Name), + c.String(utils.ConfigDirFlag.Name), + fmt.Sprintf("--%s", utils.LogDirFlag.Name), + c.String(utils.LogDirFlag.Name), + }, args...) + if err != nil { + return fmt.Errorf("error parsing command: %w", err) + } + + // Create and run a new app + app := app.CreateApp() + return app.Run(args) +} diff --git a/adapter/hd-module/set-config.go b/adapter/hd-module/set-config.go new file mode 100644 index 0000000..dc16d97 --- /dev/null +++ b/adapter/hd-module/set-config.go @@ -0,0 +1,48 @@ +package hdmodule + +import ( + "fmt" + + "github.com/nodeset-org/hyperdrive-stakewise/adapter/config" + "github.com/nodeset-org/hyperdrive-stakewise/adapter/utils" + hdconfig "github.com/nodeset-org/hyperdrive/modules/config" + "github.com/urfave/cli/v2" +) + +// Request format for `set-config` +type setConfigRequest struct { + utils.KeyedRequest + + // The config instance to process + Config map[string]any `json:"config"` +} + +// Handle the `set-config` command +func setConfig(c *cli.Context) error { + // Get the request + request, err := utils.HandleKeyedRequest[*setConfigRequest](c) + if err != nil { + return err + } + + // Get the config + cfg := config.NewExampleConfig() + err = hdconfig.UnmarshalConfigurationInstanceIntoMetadata(request.Config, cfg) + if err != nil { + return err + } + + // Make a config manager + cfgMgr, err := config.NewAdapterConfigManager(c) + if err != nil { + return fmt.Errorf("error creating config manager: %w", err) + } + cfgMgr.AdapterConfig = cfg + + // Save it + err = cfgMgr.SaveConfigToDisk() + if err != nil { + return fmt.Errorf("error saving config: %w", err) + } + return nil +} diff --git a/adapter/hd-module/version.go b/adapter/hd-module/version.go new file mode 100644 index 0000000..b4fc8c8 --- /dev/null +++ b/adapter/hd-module/version.go @@ -0,0 +1,33 @@ +package hdmodule + +import ( + "fmt" + + "github.com/goccy/go-json" + + "github.com/nodeset-org/hyperdrive-stakewise/shared" +) + +// Response format for `version` +type versionResponse struct { + // Version of the module + Version string `json:"version"` +} + +// Handle the `version` command +func version() error { + // Create the response + version := versionResponse{ + Version: shared.StakewiseVersion, + } + + // Marshal it + bytes, err := json.Marshal(version) + if err != nil { + return fmt.Errorf("error marshalling version response: %w", err) + } + + // Print it + fmt.Println(string(bytes)) + return nil +} diff --git a/adapter/utils/consts.go b/adapter/utils/consts.go new file mode 100644 index 0000000..55d7b11 --- /dev/null +++ b/adapter/utils/consts.go @@ -0,0 +1,15 @@ +package utils + +const ( + // Name of the log file for the adapter + AdapterLogFile string = "adapter.log" + + // Service log file + ServiceLogFile string = "service.log" + + // Adapter configuration file + AdapterConfigFile string = "adapter-cfg.yaml" + + // Service configuration file + ServiceConfigFile string = "service-cfg.yaml" +) diff --git a/adapter/utils/flags.go b/adapter/utils/flags.go index de93d2e..d1e4ae0 100644 --- a/adapter/utils/flags.go +++ b/adapter/utils/flags.go @@ -79,6 +79,18 @@ var ( Usage: "The path to the secret key file for authentication", Value: "/hd/secret", } + ConfigDirFlag *cli.StringFlag = &cli.StringFlag{ + Name: "config-dir", + Aliases: []string{"c"}, + Usage: "The path to the directory for module configuration files", + Value: "/hd/config", + } + LogDirFlag *cli.StringFlag = &cli.StringFlag{ + Name: "log-dir", + Aliases: []string{"l"}, + Usage: "The path to the directory for module log files", + Value: "/hd/logs", + } ) func InstantiateFlag[FlagType cli.Flag](prototype FlagType, description string) cli.Flag { diff --git a/go.mod b/go.mod index e9f72cd..d912a76 100644 --- a/go.mod +++ b/go.mod @@ -92,6 +92,7 @@ require ( github.com/herumi/bls-eth-go-binary v1.36.1 // indirect github.com/holiman/uint256 v1.3.1 // indirect github.com/ipfs/go-cid v0.4.1 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index b6af530..7e10472 100644 --- a/go.sum +++ b/go.sum @@ -286,6 +286,8 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= diff --git a/shared/consts.go b/shared/consts.go new file mode 100644 index 0000000..bcf1538 --- /dev/null +++ b/shared/consts.go @@ -0,0 +1,12 @@ +package shared + +const ( + // Name of the service container + ServiceContainerName string = "hyperdrive-stakewise" + + // Name of the log file for the service + ServiceLogFile string = "service.log" + + // Default port for the server API + DefaultServerApiPort uint16 = 8080 +)