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: Allow app developers to override default appConfig template #9550

Merged
merged 22 commits into from
Jun 23, 2021
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
8 changes: 7 additions & 1 deletion docs/core/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,13 @@ The root command (called `rootCmd`) is what the user first types into the comman

Next is an example `rootCmd` function from the `simapp` application. It instantiates the root command, adds a [_persistent_ flag](#flags) and `PreRun` function to be run before every execution, and adds all of the necessary subcommands.

+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L37-L93
+++ https://github.com/cosmos/cosmos-sdk/blob/4eea4cafd3b8b1c2cd493886db524500c9dd745c/simapp/simd/cmd/root.go#L37-L150

`rootCmd` has a function called `initAppConfig()` which is useful for setting the application's custom configs.
By default app uses Tendermint app config template from SDK, which can be over-written via `initAppConfig()`.
Here's an example code to override default `appConfig` and `app.toml` template.

+++ https://github.com/cosmos/cosmos-sdk/blob/4eea4cafd3b8b1c2cd493886db524500c9dd745c/simapp/simd/cmd/root.go#L84-L117

The root-level `status` and `keys` subcommands are common across most applications and do not interact with application state. The bulk of an application's functionality - what users can actually _do_ with it - is enabled by its `tx` and `query` commands.

Expand Down
18 changes: 15 additions & 3 deletions server/config/toml.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
tmos "github.com/tendermint/tendermint/libs/os"
)

const defaultConfigTemplate = `# This is a TOML config file.
const DefaultConfigTemplate = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml

###############################################################################
Expand Down Expand Up @@ -210,7 +210,7 @@ func init() {

tmpl := template.New("appConfigFileTemplate")

if configTemplate, err = tmpl.Parse(defaultConfigTemplate); err != nil {
if configTemplate, err = tmpl.Parse(DefaultConfigTemplate); err != nil {
panic(err)
}
}
Expand All @@ -224,9 +224,21 @@ func ParseConfig(v *viper.Viper) (*Config, error) {
return conf, err
}

// SetConfigTemplate sets the custom app config template for
// the application
func SetConfigTemplate(customTemplate string) {
anilcse marked this conversation as resolved.
Show resolved Hide resolved
var err error

tmpl := template.New("appConfigFileTemplate")

if configTemplate, err = tmpl.Parse(customTemplate); err != nil {
panic(err)
}
}

// WriteConfigFile renders config using the template and writes it to
// configFilePath.
func WriteConfigFile(configFilePath string, config *Config) {
func WriteConfigFile(configFilePath string, config interface{}) {
var buffer bytes.Buffer

if err := configTemplate.Execute(&buffer, config); err != nil {
Expand Down
5 changes: 4 additions & 1 deletion server/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ value called `rpc.laddr` would be read from an environmental variable called
Running the `InterceptConfigsPreRunHandler` also reads `app.toml`
and `config.toml` from the home directory under the `config` directory.
If `config.toml` or `app.toml` do not exist then those files are created
and populated with default values.
and populated with default values. `InterceptConfigsPreRunHandler` takes
two parameters to set/update a custom template to create custom `app.toml`.
If these parameters are empty, the server then creates a default template
provided by the SDK.
*/
package server
35 changes: 24 additions & 11 deletions server/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,13 @@ func bindFlags(basename string, cmd *cobra.Command, v *viper.Viper) (err error)
// application command. It will create a Viper literal and a default server
// Context. The server Tendermint configuration will either be read and parsed
// or created and saved to disk, where the server Context is updated to reflect
// the Tendermint configuration. The Viper literal is used to read and parse
// the application configuration. Command handlers can fetch the server Context
// to get the Tendermint configuration or to get access to Viper.
func InterceptConfigsPreRunHandler(cmd *cobra.Command) error {
// the Tendermint configuration. It takes custom app config template and config
// settings to create a custom Tendermint configuration. If the custom template
// is empty, it uses default-template provided by the server. The Viper literal
// is used to read and parse the application configuration. Command handlers can
// fetch the server Context to get the Tendermint configuration or to get access
// to Viper.
func InterceptConfigsPreRunHandler(cmd *cobra.Command, customAppConfigTemplate string, customAppConfig interface{}) error {
serverCtx := NewDefaultContext()

// Get the executable name and configure the viper instance so that environmental
Expand All @@ -123,7 +126,7 @@ func InterceptConfigsPreRunHandler(cmd *cobra.Command) error {
serverCtx.Viper.AutomaticEnv()

// intercept configuration files, using both Viper instances separately
config, err := interceptConfigs(serverCtx.Viper)
config, err := interceptConfigs(serverCtx.Viper, customAppConfigTemplate, customAppConfig)
if err != nil {
return err
}
Expand Down Expand Up @@ -181,7 +184,7 @@ func SetCmdServerContext(cmd *cobra.Command, serverCtx *Context) error {
// configuration file. The Tendermint configuration file is parsed given a root
// Viper object, whereas the application is parsed with the private package-aware
// viperCfg object.
func interceptConfigs(rootViper *viper.Viper) (*tmcfg.Config, error) {
func interceptConfigs(rootViper *viper.Viper, customAppTemplate string, customConfig interface{}) (*tmcfg.Config, error) {
rootDir := rootViper.GetString(flags.FlagHome)
configPath := filepath.Join(rootDir, "config")
tmCfgFile := filepath.Join(configPath, "config.toml")
Expand Down Expand Up @@ -226,12 +229,22 @@ func interceptConfigs(rootViper *viper.Viper) (*tmcfg.Config, error) {

appCfgFilePath := filepath.Join(configPath, "app.toml")
if _, err := os.Stat(appCfgFilePath); os.IsNotExist(err) {
appConf, err := config.ParseConfig(rootViper)
if err != nil {
return nil, fmt.Errorf("failed to parse %s: %w", appCfgFilePath, err)
}
if customAppTemplate != "" {
config.SetConfigTemplate(customAppTemplate)

if err = rootViper.Unmarshal(&customConfig); err != nil {
return nil, fmt.Errorf("failed to parse %s: %w", appCfgFilePath, err)
}

config.WriteConfigFile(appCfgFilePath, appConf)
config.WriteConfigFile(appCfgFilePath, customConfig)
} else {
appConf, err := config.ParseConfig(rootViper)
if err != nil {
return nil, fmt.Errorf("failed to parse %s: %w", appCfgFilePath, err)
}

config.WriteConfigFile(appCfgFilePath, appConf)
}
}

rootViper.SetConfigType("toml")
Expand Down
2 changes: 1 addition & 1 deletion server/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ var CancelledInPreRun = errors.New("Canelled in prerun")
// Used in each test to run the function under test via Cobra
// but to always halt the command
func preRunETestImpl(cmd *cobra.Command, args []string) error {
err := InterceptConfigsPreRunHandler(cmd)
err := InterceptConfigsPreRunHandler(cmd, "", nil)
if err != nil {
return err
}
Expand Down
46 changes: 44 additions & 2 deletions simapp/simd/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"

serverconfig "github.com/cosmos/cosmos-sdk/server/config"
"github.com/spf13/cast"
"github.com/spf13/cobra"
tmcli "github.com/tendermint/tendermint/libs/cli"
Expand All @@ -14,7 +15,7 @@ import (

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
config "github.com/cosmos/cosmos-sdk/client/config"
"github.com/cosmos/cosmos-sdk/client/config"
"github.com/cosmos/cosmos-sdk/client/debug"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/keys"
Expand Down Expand Up @@ -66,7 +67,9 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
return err
}

return server.InterceptConfigsPreRunHandler(cmd)
customAppTemplate, customAppConfig := initAppConfig()

return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig)
},
}

Expand All @@ -75,6 +78,45 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
return rootCmd, encodingConfig
}

// initAppConfig helps to override default appConfig template and configs.
// return "", nil if no custom configuration is required for the application.
func initAppConfig() (string, interface{}) {
Copy link
Member

Choose a reason for hiding this comment

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

@anilcse do you think this could be reused to override tendermint config.toml?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I believe we can do that. I will look into it tomorrow and create a PR

Copy link
Member

Choose a reason for hiding this comment

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

// The following code snippet is just for reference.

// WASMConfig defines configuration for the wasm module.
type WASMConfig struct {
// This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries
QueryGasLimit uint64 `mapstructure:"query_gas_limit"`

// Address defines the gRPC-web server to listen on
LruSize uint64 `mapstructure:"lru_size"`
}

type CustomAppConfig struct {
serverconfig.Config

WASM WASMConfig `mapstructure:"wasm"`
}

customAppConfig := CustomAppConfig{
Config: *serverconfig.DefaultConfig(),
WASM: WASMConfig{
LruSize: 1,
QueryGasLimit: 300000,
},
}

customAppTemplate := serverconfig.DefaultConfigTemplate + `
[wasm]
# This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries
query_gas_limit = 300000
# This is the number of wasm vm instances we keep cached in memory for speed-up
# Warning: this is currently unstable and may lead to crashes, best to keep for 0 unless testing locally
lru_size = 0`

return customAppTemplate, customAppConfig
}

func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
cfg := sdk.GetConfig()
cfg.Seal()
Expand Down