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

Read config values from env on init command #663

Merged
merged 21 commits into from
Feb 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ed30ac1
Add ApplyEnvVars() to ComponentConfig interface
roignpar Feb 7, 2019
032f028
Implement ApplyEnvVars for all ComponentConfigs
roignpar Feb 8, 2019
21a5391
Remove json config overriding with env vars
roignpar Feb 8, 2019
da58eae
Add ipfsproxy NodeHTTPS config field to ToJSON
roignpar Feb 11, 2019
15ac8cd
Use ParseDurations when applying json to cluster Config
roignpar Feb 11, 2019
580020d
Use separate envConfigKeys for metrics and tracing
roignpar Feb 13, 2019
aa5d545
Implement ApplyEnvVars for ipfshttp Config
roignpar Feb 13, 2019
47252f8
Merge remote-tracking branch 'upstream/master' into issue_656
roignpar Feb 15, 2019
168cf76
Change ApplyEnvVars strategy for all config components
roignpar Feb 15, 2019
523e109
Create LoadJSONFileAndEnv config method for convenience
roignpar Feb 15, 2019
78ac49a
Fix env config tests
roignpar Feb 18, 2019
36ee0f8
Add ApplyEnvVars test to ipfsproxy config
roignpar Feb 18, 2019
bac982c
Add ApplyEnvVars test to raft config
roignpar Feb 18, 2019
08580c3
Add ApplyEnvVars test to disk config
roignpar Feb 18, 2019
e38ceab
Add ApplyEnvVars test to numpin config
roignpar Feb 18, 2019
be8c56f
Add ApplyEnvVars test to ipfshttp config
roignpar Feb 18, 2019
40d1077
Add ApplyEnvVars test to monbasic config
roignpar Feb 18, 2019
38886da
Add ApplyEnvVars test to pubsubmon config
roignpar Feb 18, 2019
368f1de
Add ApplyEnvVars test to maptracker config
roignpar Feb 18, 2019
50844b9
Add ApplyEnvVars test to stateless config
roignpar Feb 18, 2019
06482e5
Add ApplyEnvVars test to observations config
roignpar Feb 18, 2019
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
40 changes: 32 additions & 8 deletions api/ipfsproxy/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ type Config struct {
type jsonConfig struct {
ListenMultiaddress string `json:"listen_multiaddress"`
NodeMultiaddress string `json:"node_multiaddress"`
NodeHTTPS string `json:"node_https,omitempty"`
NodeHTTPS bool `json:"node_https,omitempty"`

ReadTimeout string `json:"read_timeout"`
ReadHeaderTimeout string `json:"read_header_timeout"`
Expand Down Expand Up @@ -154,6 +154,22 @@ func (cfg *Config) Default() error {
return nil
}

// ApplyEnvVars fills in any Config fields found
// as environment variables.
func (cfg *Config) ApplyEnvVars() error {
jcfg, err := cfg.toJSONConfig()
if err != nil {
return err
}

err = envconfig.Process(envConfigKey, jcfg)
if err != nil {
return err
}

return cfg.applyJSONConfig(jcfg)
}

// Validate checks that the fields of this Config have sensible values,
// at least in appearance.
func (cfg *Config) Validate() error {
Expand Down Expand Up @@ -210,12 +226,10 @@ func (cfg *Config) LoadJSON(raw []byte) error {
return fmt.Errorf("error setting config to default values: %s", err)
}

// override json config with env var
err = envconfig.Process("cluster_ipfsproxy", jcfg)
if err != nil {
return err
}
return cfg.applyJSONConfig(jcfg)
}

func (cfg *Config) applyJSONConfig(jcfg *jsonConfig) error {
proxyAddr, err := ma.NewMultiaddr(jcfg.ListenMultiaddress)
if err != nil {
return fmt.Errorf("error parsing proxy listen_multiaddress: %s", err)
Expand Down Expand Up @@ -251,14 +265,24 @@ func (cfg *Config) LoadJSON(raw []byte) error {

// ToJSON generates a human-friendly JSON representation of this Config.
func (cfg *Config) ToJSON() (raw []byte, err error) {
jcfg, err := cfg.toJSONConfig()
if err != nil {
return
}

raw, err = config.DefaultJSONMarshal(jcfg)
return
}

func (cfg *Config) toJSONConfig() (jcfg *jsonConfig, err error) {
// Multiaddress String() may panic
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%s", r)
}
}()

jcfg := &jsonConfig{}
jcfg = &jsonConfig{}

// Set all configuration fields
jcfg.ListenMultiaddress = cfg.ListenAddr.String()
Expand All @@ -267,11 +291,11 @@ func (cfg *Config) ToJSON() (raw []byte, err error) {
jcfg.ReadHeaderTimeout = cfg.ReadHeaderTimeout.String()
jcfg.WriteTimeout = cfg.WriteTimeout.String()
jcfg.IdleTimeout = cfg.IdleTimeout.String()
jcfg.NodeHTTPS = cfg.NodeHTTPS

jcfg.ExtractHeadersExtra = cfg.ExtractHeadersExtra
jcfg.ExtractHeadersPath = cfg.ExtractHeadersPath
jcfg.ExtractHeadersTTL = cfg.ExtractHeadersTTL.String()

raw, err = config.DefaultJSONMarshal(jcfg)
return
}
13 changes: 13 additions & 0 deletions api/ipfsproxy/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package ipfsproxy

import (
"encoding/json"
"os"
"testing"
"time"
)

var cfgJSON = []byte(`
Expand Down Expand Up @@ -125,3 +127,14 @@ func TestDefault(t *testing.T) {
t.Fatal("expected error validating")
}
}

func TestApplyEnvVars(t *testing.T) {
os.Setenv("CLUSTER_IPFSPROXY_IDLETIMEOUT", "22s")
cfg := &Config{}
cfg.Default()
cfg.ApplyEnvVars()

if cfg.IdleTimeout != 22*time.Second {
t.Error("failed to override idle_timeout with env var")
}
}
39 changes: 31 additions & 8 deletions api/rest/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,22 @@ func (cfg *Config) Default() error {
return nil
}

// ApplyEnvVars fills in any Config fields found
// as environment variables.
func (cfg *Config) ApplyEnvVars() error {
jcfg, err := cfg.toJSONConfig()
if err != nil {
return err
}

err = envconfig.Process(envConfigKey, jcfg)
if err != nil {
return err
}

return cfg.applyJSONConfig(jcfg)
}

// Validate makes sure that all fields in this Config have
// working values, at least in appearance.
func (cfg *Config) Validate() error {
Expand Down Expand Up @@ -230,13 +246,11 @@ func (cfg *Config) LoadJSON(raw []byte) error {

cfg.Default()

// override json config with env var
err = envconfig.Process(envConfigKey, jcfg)
if err != nil {
return err
}
return cfg.applyJSONConfig(jcfg)
}

err = cfg.loadHTTPOptions(jcfg)
func (cfg *Config) applyJSONConfig(jcfg *jsonConfig) error {
err := cfg.loadHTTPOptions(jcfg)
if err != nil {
return err
}
Expand Down Expand Up @@ -361,14 +375,24 @@ func (cfg *Config) loadLibp2pOptions(jcfg *jsonConfig) error {
// ToJSON produce a human-friendly JSON representation of the Config
// object.
func (cfg *Config) ToJSON() (raw []byte, err error) {
jcfg, err := cfg.toJSONConfig()
if err != nil {
return
}

raw, err = config.DefaultJSONMarshal(jcfg)
return
}

func (cfg *Config) toJSONConfig() (jcfg *jsonConfig, err error) {
// Multiaddress String() may panic
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%s", r)
}
}()

jcfg := &jsonConfig{
jcfg = &jsonConfig{
HTTPListenMultiaddress: cfg.HTTPListenAddr.String(),
SSLCertFile: cfg.pathSSLCertFile,
SSLKeyFile: cfg.pathSSLKeyFile,
Expand Down Expand Up @@ -400,7 +424,6 @@ func (cfg *Config) ToJSON() (raw []byte, err error) {
jcfg.Libp2pListenMultiaddress = cfg.Libp2pListenAddr.String()
}

raw, err = config.DefaultJSONMarshal(jcfg)
return
}

Expand Down
5 changes: 3 additions & 2 deletions api/rest/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,15 @@ func TestLoadJSON(t *testing.T) {
}
}

func TestLoadJSONEnvConfig(t *testing.T) {
func TestApplyEnvVars(t *testing.T) {
username := "admin"
password := "thisaintmypassword"
user1 := "user1"
user1pass := "user1passwd"
os.Setenv("CLUSTER_RESTAPI_BASICAUTHCREDS", username+":"+password+","+user1+":"+user1pass)
cfg := &Config{}
err := cfg.LoadJSON(cfgJSON)
cfg.Default()
err := cfg.ApplyEnvVars()
if err != nil {
t.Fatal(err)
}
Expand Down
63 changes: 39 additions & 24 deletions cluster_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,22 @@ func (cfg *Config) Default() error {
return nil
}

// ApplyEnvVars fills in any Config fields found
// as environment variables.
func (cfg *Config) ApplyEnvVars() error {
jcfg, err := cfg.toConfigJSON()
if err != nil {
return err
}

err = envconfig.Process(cfg.ConfigKey(), jcfg)
if err != nil {
return err
}

return cfg.applyConfigJSON(jcfg)
}

// Validate will check that the values of this config
// seem to be working ones.
func (cfg *Config) Validate() error {
Expand Down Expand Up @@ -305,20 +321,10 @@ for more information.`)
return errors.New("cluster.Peers and cluster.Bootstrap keys have been deprecated")
}

// override json config with env var
err = envconfig.Process(cfg.ConfigKey(), jcfg)
if err != nil {
return err
}

parseDuration := func(txt string) time.Duration {
d, _ := time.ParseDuration(txt)
if txt != "" && d == 0 {
logger.Warningf("%s is not a valid duration. Default will be used", txt)
}
return d
}
return cfg.applyConfigJSON(jcfg)
}

func (cfg *Config) applyConfigJSON(jcfg *configJSON) error {
config.SetIfNotDefault(jcfg.PeerstoreFile, &cfg.PeerstoreFile)

id, err := peer.IDB58Decode(jcfg.ID)
Expand Down Expand Up @@ -365,15 +371,15 @@ for more information.`)
config.SetIfNotDefault(rplMin, &cfg.ReplicationFactorMin)
config.SetIfNotDefault(rplMax, &cfg.ReplicationFactorMax)

stateSyncInterval := parseDuration(jcfg.StateSyncInterval)
ipfsSyncInterval := parseDuration(jcfg.IPFSSyncInterval)
monitorPingInterval := parseDuration(jcfg.MonitorPingInterval)
peerWatchInterval := parseDuration(jcfg.PeerWatchInterval)

config.SetIfNotDefault(stateSyncInterval, &cfg.StateSyncInterval)
config.SetIfNotDefault(ipfsSyncInterval, &cfg.IPFSSyncInterval)
config.SetIfNotDefault(monitorPingInterval, &cfg.MonitorPingInterval)
config.SetIfNotDefault(peerWatchInterval, &cfg.PeerWatchInterval)
err = config.ParseDurations("cluster",
&config.DurationOpt{Duration: jcfg.StateSyncInterval, Dst: &cfg.StateSyncInterval, Name: "state_sync_interval"},
&config.DurationOpt{Duration: jcfg.IPFSSyncInterval, Dst: &cfg.IPFSSyncInterval, Name: "ipfs_sync_interval"},
&config.DurationOpt{Duration: jcfg.MonitorPingInterval, Dst: &cfg.MonitorPingInterval, Name: "monitor_ping_interval"},
&config.DurationOpt{Duration: jcfg.PeerWatchInterval, Dst: &cfg.PeerWatchInterval, Name: "peer_watch_interval"},
)
if err != nil {
return err
}

cfg.LeaveOnShutdown = jcfg.LeaveOnShutdown
cfg.DisableRepinning = jcfg.DisableRepinning
Expand All @@ -383,14 +389,24 @@ for more information.`)

// ToJSON generates a human-friendly version of Config.
func (cfg *Config) ToJSON() (raw []byte, err error) {
jcfg, err := cfg.toConfigJSON()
if err != nil {
return
}

raw, err = json.MarshalIndent(jcfg, "", " ")
return
}

func (cfg *Config) toConfigJSON() (jcfg *configJSON, err error) {
// Multiaddress String() may panic
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("%s", r)
}
}()

jcfg := &configJSON{}
jcfg = &configJSON{}

// Private Key
pkeyBytes, err := cfg.PrivateKey.Bytes()
Expand All @@ -415,7 +431,6 @@ func (cfg *Config) ToJSON() (raw []byte, err error) {
jcfg.DisableRepinning = cfg.DisableRepinning
jcfg.PeerstoreFile = cfg.PeerstoreFile

raw, err = json.MarshalIndent(jcfg, "", " ")
return
}

Expand Down
19 changes: 10 additions & 9 deletions cluster_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,15 +187,6 @@ func TestLoadJSON(t *testing.T) {
t.Error("expected default replication factors")
}
})

t.Run("env var override", func(t *testing.T) {
os.Setenv("CLUSTER_PEERNAME", "envsetpeername")
cfg := &Config{}
cfg.LoadJSON(ccfgTestJSON)
if cfg.Peername != "envsetpeername" {
t.Fatal("failed to override peername with env var")
}
})
}

func TestToJSON(t *testing.T) {
Expand All @@ -220,6 +211,16 @@ func TestDefault(t *testing.T) {
}
}

func TestApplyEnvVars(t *testing.T) {
os.Setenv("CLUSTER_PEERNAME", "envsetpeername")
cfg := &Config{}
cfg.Default()
cfg.ApplyEnvVars()
if cfg.Peername != "envsetpeername" {
t.Fatal("failed to override peername with env var")
}
}

func TestValidate(t *testing.T) {
cfg := &Config{}
cfg.Default()
Expand Down
2 changes: 1 addition & 1 deletion cmd/ipfs-cluster-service/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func daemon(c *cli.Context) error {
// always wait for configuration to be saved
defer cfgMgr.Shutdown()

err = cfgMgr.LoadJSONFromFile(configPath)
err = cfgMgr.LoadJSONFileAndEnv(configPath)
checkErr("loading configuration", err)

if c.Bool("stats") {
Expand Down
19 changes: 9 additions & 10 deletions cmd/ipfs-cluster-service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ configuration.
err := cfgMgr.Default()
checkErr("generating default configuration", err)

err = cfgMgr.ApplyEnvVars()
checkErr("applying environment variables to configuration", err)

// Set user secret
if userSecretDefined {
cfgs.clusterCfg.Secret = userSecret
Expand Down Expand Up @@ -439,7 +442,7 @@ the mth data folder (m currently defaults to 5)
}

cfgMgr, cfgs := makeConfigs()
err = cfgMgr.LoadJSONFromFile(configPath)
err = cfgMgr.LoadJSONFileAndEnv(configPath)
checkErr("reading configuration", err)

err = cleanupState(cfgs.consensusCfg)
Expand Down Expand Up @@ -501,18 +504,14 @@ func setupDebug() {
}

func userProvidedSecret(enterSecret bool) ([]byte, bool) {
var secret string
if enterSecret {
secret = promptUser("Enter cluster secret (32-byte hex string): ")
} else if envSecret, envSecretDefined := os.LookupEnv("CLUSTER_SECRET"); envSecretDefined {
secret = envSecret
} else {
return nil, false
secret := promptUser("Enter cluster secret (32-byte hex string): ")
decodedSecret, err := ipfscluster.DecodeClusterSecret(secret)
checkErr("parsing user-provided secret", err)
return decodedSecret, true
}

decodedSecret, err := ipfscluster.DecodeClusterSecret(secret)
checkErr("parsing user-provided secret", err)
return decodedSecret, true
return nil, false
}

func promptUser(msg string) string {
Expand Down
Loading