diff options
author | holisticode <holistic.computing@gmail.com> | 2017-12-12 05:56:06 +0800 |
---|---|---|
committer | Felix Lange <fjl@users.noreply.github.com> | 2017-12-12 05:56:06 +0800 |
commit | 32516c768ec09e2a71cab5983d2c8b8ae5d92fc7 (patch) | |
tree | 9f02126fd6f219163776ad1ab8a8b924a32811a4 /cmd/swarm/config.go | |
parent | 1a32bdf92cceb7a42e5636e12d95609e17b8f786 (diff) | |
download | dexon-32516c768ec09e2a71cab5983d2c8b8ae5d92fc7.tar dexon-32516c768ec09e2a71cab5983d2c8b8ae5d92fc7.tar.gz dexon-32516c768ec09e2a71cab5983d2c8b8ae5d92fc7.tar.bz2 dexon-32516c768ec09e2a71cab5983d2c8b8ae5d92fc7.tar.lz dexon-32516c768ec09e2a71cab5983d2c8b8ae5d92fc7.tar.xz dexon-32516c768ec09e2a71cab5983d2c8b8ae5d92fc7.tar.zst dexon-32516c768ec09e2a71cab5983d2c8b8ae5d92fc7.zip |
cmd/swarm: add config file (#15548)
This commit adds a TOML configuration option to swarm. It reuses
the TOML configuration structure used in geth with swarm
customized items.
The commit:
* Adds a "dumpconfig" command to the swarm executable which
allows printing the (default) configuration to stdout, which
then can be redirected to a file in order to customize it.
* Adds a "--config <file>" option to the swarm executable which will
allow to load a configuration file in TOML format from the
specified location in order to initialize the Swarm node The
override priorities are like follows: environment variables
override command line arguments override config file override
default config.
Diffstat (limited to 'cmd/swarm/config.go')
-rw-r--r-- | cmd/swarm/config.go | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/cmd/swarm/config.go b/cmd/swarm/config.go new file mode 100644 index 000000000..ec81bc741 --- /dev/null +++ b/cmd/swarm/config.go @@ -0,0 +1,321 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of go-ethereum. +// +// go-ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// go-ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. + +package main + +import ( + "errors" + "fmt" + "io" + "os" + "reflect" + "strconv" + "unicode" + + cli "gopkg.in/urfave/cli.v1" + + "github.com/ethereum/go-ethereum/cmd/utils" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/node" + "github.com/naoina/toml" + + bzzapi "github.com/ethereum/go-ethereum/swarm/api" +) + +var ( + //flag definition for the dumpconfig command + DumpConfigCommand = cli.Command{ + Action: utils.MigrateFlags(dumpConfig), + Name: "dumpconfig", + Usage: "Show configuration values", + ArgsUsage: "", + Flags: app.Flags, + Category: "MISCELLANEOUS COMMANDS", + Description: `The dumpconfig command shows configuration values.`, + } + + //flag definition for the config file command + SwarmTomlConfigPathFlag = cli.StringFlag{ + Name: "config", + Usage: "TOML configuration file", + } +) + +//constants for environment variables +const ( + SWARM_ENV_CHEQUEBOOK_ADDR = "SWARM_CHEQUEBOOK_ADDR" + SWARM_ENV_ACCOUNT = "SWARM_ACCOUNT" + SWARM_ENV_LISTEN_ADDR = "SWARM_LISTEN_ADDR" + SWARM_ENV_PORT = "SWARM_PORT" + SWARM_ENV_NETWORK_ID = "SWARM_NETWORK_ID" + SWARM_ENV_SWAP_ENABLE = "SWARM_SWAP_ENABLE" + SWARM_ENV_SWAP_API = "SWARM_SWAP_API" + SWARM_ENV_SYNC_ENABLE = "SWARM_SYNC_ENABLE" + SWARM_ENV_ENS_API = "SWARM_ENS_API" + SWARM_ENV_ENS_ADDR = "SWARM_ENS_ADDR" + SWARM_ENV_CORS = "SWARM_CORS" + SWARM_ENV_BOOTNODES = "SWARM_BOOTNODES" + GETH_ENV_DATADIR = "GETH_DATADIR" +) + +// These settings ensure that TOML keys use the same names as Go struct fields. +var tomlSettings = toml.Config{ + NormFieldName: func(rt reflect.Type, key string) string { + return key + }, + FieldToKey: func(rt reflect.Type, field string) string { + return field + }, + MissingField: func(rt reflect.Type, field string) error { + link := "" + if unicode.IsUpper(rune(rt.Name()[0])) && rt.PkgPath() != "main" { + link = fmt.Sprintf(", check github.com/ethereum/go-ethereum/swarm/api/config.go for available fields") + } + return fmt.Errorf("field '%s' is not defined in %s%s", field, rt.String(), link) + }, +} + +//before booting the swarm node, build the configuration +func buildConfig(ctx *cli.Context) (config *bzzapi.Config, err error) { + //check for deprecated flags + checkDeprecated(ctx) + //start by creating a default config + config = bzzapi.NewDefaultConfig() + //first load settings from config file (if provided) + config, err = configFileOverride(config, ctx) + //override settings provided by environment variables + config = envVarsOverride(config) + //override settings provided by command line + config = cmdLineOverride(config, ctx) + + return +} + +//finally, after the configuration build phase is finished, initialize +func initSwarmNode(config *bzzapi.Config, stack *node.Node, ctx *cli.Context) { + //at this point, all vars should be set in the Config + //get the account for the provided swarm account + prvkey := getAccount(config.BzzAccount, ctx, stack) + //set the resolved config path (geth --datadir) + config.Path = stack.InstanceDir() + //finally, initialize the configuration + config.Init(prvkey) + //configuration phase completed here + log.Debug("Starting Swarm with the following parameters:") + //after having created the config, print it to screen + log.Debug(printConfig(config)) +} + +//override the current config with whatever is in the config file, if a config file has been provided +func configFileOverride(config *bzzapi.Config, ctx *cli.Context) (*bzzapi.Config, error) { + var err error + + //only do something if the -config flag has been set + if ctx.GlobalIsSet(SwarmTomlConfigPathFlag.Name) { + var filepath string + if filepath = ctx.GlobalString(SwarmTomlConfigPathFlag.Name); filepath == "" { + utils.Fatalf("Config file flag provided with invalid file path") + } + f, err := os.Open(filepath) + if err != nil { + return nil, err + } + defer f.Close() + + //decode the TOML file into a Config struct + //note that we are decoding into the existing defaultConfig; + //if an entry is not present in the file, the default entry is kept + err = tomlSettings.NewDecoder(f).Decode(&config) + // Add file name to errors that have a line number. + if _, ok := err.(*toml.LineError); ok { + err = errors.New(filepath + ", " + err.Error()) + } + } + return config, err +} + +//override the current config with whatever is provided through the command line +//most values are not allowed a zero value (empty string), if not otherwise noted +func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Config { + + if keyid := ctx.GlobalString(SwarmAccountFlag.Name); keyid != "" { + currentConfig.BzzAccount = keyid + } + + if chbookaddr := ctx.GlobalString(ChequebookAddrFlag.Name); chbookaddr != "" { + currentConfig.Contract = common.HexToAddress(chbookaddr) + } + + if networkid := ctx.GlobalString(SwarmNetworkIdFlag.Name); networkid != "" { + if id, _ := strconv.Atoi(networkid); id != 0 { + currentConfig.NetworkId = uint64(id) + } + } + + if ctx.GlobalIsSet(utils.DataDirFlag.Name) { + if datadir := ctx.GlobalString(utils.DataDirFlag.Name); datadir != "" { + currentConfig.Path = datadir + } + } + + bzzport := ctx.GlobalString(SwarmPortFlag.Name) + if len(bzzport) > 0 { + currentConfig.Port = bzzport + } + + if bzzaddr := ctx.GlobalString(SwarmListenAddrFlag.Name); bzzaddr != "" { + currentConfig.ListenAddr = bzzaddr + } + + if ctx.GlobalIsSet(SwarmSwapEnabledFlag.Name) { + currentConfig.SwapEnabled = true + } + + if ctx.GlobalIsSet(SwarmSyncEnabledFlag.Name) { + currentConfig.SyncEnabled = true + } + + currentConfig.SwapApi = ctx.GlobalString(SwarmSwapAPIFlag.Name) + if currentConfig.SwapEnabled && currentConfig.SwapApi == "" { + utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API) + } + + //EnsApi can be set to "", so can't check for empty string, as it is allowed! + if ctx.GlobalIsSet(EnsAPIFlag.Name) { + currentConfig.EnsApi = ctx.GlobalString(EnsAPIFlag.Name) + } + + if ensaddr := ctx.GlobalString(EnsAddrFlag.Name); ensaddr != "" { + currentConfig.EnsRoot = common.HexToAddress(ensaddr) + } + + if cors := ctx.GlobalString(CorsStringFlag.Name); cors != "" { + currentConfig.Cors = cors + } + + if ctx.GlobalIsSet(utils.BootnodesFlag.Name) { + currentConfig.BootNodes = ctx.GlobalString(utils.BootnodesFlag.Name) + } + + return currentConfig + +} + +//override the current config with whatver is provided in environment variables +//most values are not allowed a zero value (empty string), if not otherwise noted +func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) { + + if keyid := os.Getenv(SWARM_ENV_ACCOUNT); keyid != "" { + currentConfig.BzzAccount = keyid + } + + if chbookaddr := os.Getenv(SWARM_ENV_CHEQUEBOOK_ADDR); chbookaddr != "" { + currentConfig.Contract = common.HexToAddress(chbookaddr) + } + + if networkid := os.Getenv(SWARM_ENV_NETWORK_ID); networkid != "" { + if id, _ := strconv.Atoi(networkid); id != 0 { + currentConfig.NetworkId = uint64(id) + } + } + + if datadir := os.Getenv(GETH_ENV_DATADIR); datadir != "" { + currentConfig.Path = datadir + } + + bzzport := os.Getenv(SWARM_ENV_PORT) + if len(bzzport) > 0 { + currentConfig.Port = bzzport + } + + if bzzaddr := os.Getenv(SWARM_ENV_LISTEN_ADDR); bzzaddr != "" { + currentConfig.ListenAddr = bzzaddr + } + + if swapenable := os.Getenv(SWARM_ENV_SWAP_ENABLE); swapenable != "" { + if swap, err := strconv.ParseBool(swapenable); err != nil { + currentConfig.SwapEnabled = swap + } + } + + if syncenable := os.Getenv(SWARM_ENV_SYNC_ENABLE); syncenable != "" { + if sync, err := strconv.ParseBool(syncenable); err != nil { + currentConfig.SyncEnabled = sync + } + } + + if swapapi := os.Getenv(SWARM_ENV_SWAP_API); swapapi != "" { + currentConfig.SwapApi = swapapi + } + + if currentConfig.SwapEnabled && currentConfig.SwapApi == "" { + utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API) + } + + //EnsApi can be set to "", so can't check for empty string, as it is allowed + if ensapi, exists := os.LookupEnv(SWARM_ENV_ENS_API); exists == true { + currentConfig.EnsApi = ensapi + } + + if ensaddr := os.Getenv(SWARM_ENV_ENS_ADDR); ensaddr != "" { + currentConfig.EnsRoot = common.HexToAddress(ensaddr) + } + + if cors := os.Getenv(SWARM_ENV_CORS); cors != "" { + currentConfig.Cors = cors + } + + if bootnodes := os.Getenv(SWARM_ENV_BOOTNODES); bootnodes != "" { + currentConfig.BootNodes = bootnodes + } + + return currentConfig +} + +// dumpConfig is the dumpconfig command. +// writes a default config to STDOUT +func dumpConfig(ctx *cli.Context) error { + cfg, err := buildConfig(ctx) + if err != nil { + utils.Fatalf(fmt.Sprintf("Uh oh - dumpconfig triggered an error %v", err)) + } + comment := "" + out, err := tomlSettings.Marshal(&cfg) + if err != nil { + return err + } + io.WriteString(os.Stdout, comment) + os.Stdout.Write(out) + return nil +} + +//deprecated flags checked here +func checkDeprecated(ctx *cli.Context) { + // exit if the deprecated --ethapi flag is set + if ctx.GlobalString(DeprecatedEthAPIFlag.Name) != "" { + utils.Fatalf("--ethapi is no longer a valid command line flag, please use --ens-api and/or --swap-api.") + } +} + +//print a Config as string +func printConfig(config *bzzapi.Config) string { + out, err := tomlSettings.Marshal(&config) + if err != nil { + return (fmt.Sprintf("Something is not right with the configuration: %v", err)) + } + return string(out) +} |