diff --git a/cluster_config.go b/cluster_config.go index 873d5803..c9268771 100644 --- a/cluster_config.go +++ b/cluster_config.go @@ -11,6 +11,8 @@ import ( "sync" "time" + "github.com/kelseyhightower/envconfig" + "github.com/ipfs/ipfs-cluster/config" crypto "github.com/libp2p/go-libp2p-crypto" @@ -240,8 +242,7 @@ func isReplicationFactorValid(rplMin, rplMax int) error { return errors.New("cluster.replication_factor_max is wrong") } - if (rplMin == -1 && rplMax != -1) || - (rplMin != -1 && rplMax == -1) { + if (rplMin == -1 && rplMax != -1) || (rplMin != -1 && rplMax == -1) { return errors.New("cluster.replication_factor_min and max must be -1 when one of them is") } return nil @@ -272,6 +273,8 @@ func (cfg *Config) setDefaults() { // sets the Config fields from it. Note that it should be JSON // as generated by ToJSON(). func (cfg *Config) LoadJSON(raw []byte) error { + cfg.setDefaults() + jcfg := &configJSON{} err := json.Unmarshal(raw, jcfg) if err != nil { @@ -279,10 +282,6 @@ func (cfg *Config) LoadJSON(raw []byte) error { return err } - // Make sure all non-defined keys have good values. - cfg.setDefaults() - config.SetIfNotDefault(jcfg.PeerstoreFile, &cfg.PeerstoreFile) - if jcfg.Peers != nil || jcfg.Bootstrap != nil { logger.Error(` Your configuration is using cluster.Peers and/or cluster.Bootstrap @@ -303,6 +302,12 @@ 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 { @@ -311,6 +316,8 @@ for more information.`) return d } + config.SetIfNotDefault(jcfg.PeerstoreFile, &cfg.PeerstoreFile) + id, err := peer.IDB58Decode(jcfg.ID) if err != nil { err = fmt.Errorf("error decoding cluster ID: %s", err) diff --git a/cluster_config_test.go b/cluster_config_test.go index 004be665..2af5ec0b 100644 --- a/cluster_config_test.go +++ b/cluster_config_test.go @@ -2,6 +2,7 @@ package ipfscluster import ( "encoding/json" + "os" "testing" ) @@ -23,118 +24,178 @@ var ccfgTestJSON = []byte(` `) func TestLoadJSON(t *testing.T) { - cfg := &Config{} - err := cfg.LoadJSON(ccfgTestJSON) - if err != nil { - t.Fatal(err) + loadJSON := func(t *testing.T) (*Config, error) { + cfg := &Config{} + err := cfg.LoadJSON(ccfgTestJSON) + if err != nil { + return cfg, err + } + return cfg, nil } - if cfg.Peername != "testpeer" { - t.Error("expected peername 'testpeer'") + t.Run("basic", func(t *testing.T) { + cfg := &Config{} + err := cfg.LoadJSON(ccfgTestJSON) + if err != nil { + t.Fatal(err) + } + }) + + t.Run("peername", func(t *testing.T) { + cfg, err := loadJSON(t) + if err != nil { + t.Error(err) + } + if cfg.Peername != "testpeer" { + t.Error("expected peername 'testpeer'") + } + }) + + t.Run("expected replication factor", func(t *testing.T) { + cfg, err := loadJSON(t) + if err != nil { + t.Error(err) + } + if cfg.ReplicationFactorMin != 5 { + t.Error("expected replication factor min == 5") + } + }) + + t.Run("expected disable_repinning", func(t *testing.T) { + cfg, err := loadJSON(t) + if err != nil { + t.Error(err) + } + if !cfg.DisableRepinning { + t.Error("expected disable_repinning to be true") + } + }) + + loadJSON2 := func(t *testing.T, f func(j *configJSON)) (*Config, error) { + cfg := &Config{} + j := &configJSON{} + json.Unmarshal(ccfgTestJSON, j) + f(j) + tst, err := json.Marshal(j) + if err != nil { + return cfg, err + } + err = cfg.LoadJSON(tst) + if err != nil { + return cfg, err + } + return cfg, nil } - if cfg.ReplicationFactorMin != 5 { - t.Error("expected replication factor min == 5") - } + t.Run("bad id", func(t *testing.T) { + _, err := loadJSON2(t, func(j *configJSON) { j.ID = "abc" }) + if err == nil { + t.Error("expected error decoding ID") + } + }) - if !cfg.DisableRepinning { - t.Error("expected disable_repinning to be true") - } + t.Run("empty default peername", func(t *testing.T) { + cfg, err := loadJSON2(t, func(j *configJSON) { j.Peername = "" }) + if err != nil { + t.Error(err) + } + if cfg.Peername == "" { + t.Error("expected default peername") + } + }) - j := &configJSON{} + t.Run("bad private key", func(t *testing.T) { + _, err := loadJSON2(t, func(j *configJSON) { j.PrivateKey = "abc" }) + if err == nil { + t.Error("expected error parsing private key") + } + }) - json.Unmarshal(ccfgTestJSON, j) - j.ID = "abc" - tst, _ := json.Marshal(j) - err = cfg.LoadJSON(tst) - if err == nil { - t.Error("expected error decoding ID") - } + t.Run("bad listen multiaddress", func(t *testing.T) { + _, err := loadJSON2(t, func(j *configJSON) { j.ListenMultiaddress = "abc" }) + if err == nil { + t.Error("expected error parsing listen_multiaddress") + } + }) - j = &configJSON{} - json.Unmarshal(ccfgTestJSON, j) - j.Peername = "" - tst, _ = json.Marshal(j) - err = cfg.LoadJSON(tst) - if cfg.Peername == "" { - t.Error("expected default peername") - } + t.Run("bad secret", func(t *testing.T) { + _, err := loadJSON2(t, func(j *configJSON) { j.Secret = "abc" }) + if err == nil { + t.Error("expected error decoding secret") + } + }) - j = &configJSON{} - json.Unmarshal(ccfgTestJSON, j) - j.PrivateKey = "abc" - tst, _ = json.Marshal(j) - err = cfg.LoadJSON(tst) - if err == nil { - t.Error("expected error parsing private key") - } + t.Run("default replication factors", func(t *testing.T) { + cfg, err := loadJSON2( + t, + func(j *configJSON) { + j.ReplicationFactor = 0 + j.ReplicationFactorMin = 0 + j.ReplicationFactorMax = 0 + }, + ) + if err != nil { + t.Error(err) + } + if cfg.ReplicationFactorMin != -1 || cfg.ReplicationFactorMax != -1 { + t.Error("expected default replication factor") + } + }) - j = &configJSON{} - json.Unmarshal(ccfgTestJSON, j) - j.ListenMultiaddress = "abc" - tst, _ = json.Marshal(j) - err = cfg.LoadJSON(tst) - if err == nil { - t.Error("expected error parsing listen_multiaddress") - } + t.Run("replication factor min/max override", func(t *testing.T) { + cfg, err := loadJSON2(t, func(j *configJSON) { j.ReplicationFactor = 3 }) + if err != nil { + t.Error(err) + } + if cfg.ReplicationFactorMin != 3 || cfg.ReplicationFactorMax != 3 { + t.Error("expected replicationFactor Min/Max override") + } + }) - j = &configJSON{} - json.Unmarshal(ccfgTestJSON, j) - j.Secret = "abc" - tst, _ = json.Marshal(j) - err = cfg.LoadJSON(tst) - if err == nil { - t.Error("expected error decoding secret") - } + t.Run("only replication factor min set to -1", func(t *testing.T) { + _, err := loadJSON2(t, func(j *configJSON) { j.ReplicationFactorMin = -1 }) + if err == nil { + t.Error("expected error when only one replication factor is -1") + } + }) - j = &configJSON{} - json.Unmarshal(ccfgTestJSON, j) - j.ReplicationFactor = 0 - j.ReplicationFactorMin = 0 - j.ReplicationFactorMax = 0 - tst, _ = json.Marshal(j) - cfg.LoadJSON(tst) - if cfg.ReplicationFactorMin != -1 || cfg.ReplicationFactorMax != -1 { - t.Error("expected default replication factor") - } + t.Run("replication factor min > max", func(t *testing.T) { + _, err := loadJSON2( + t, + func(j *configJSON) { + j.ReplicationFactorMin = 5 + j.ReplicationFactorMax = 4 + }, + ) + if err == nil { + t.Error("expected error when only rplMin > rplMax") + } + }) - j = &configJSON{} - json.Unmarshal(ccfgTestJSON, j) - j.ReplicationFactor = 3 - tst, _ = json.Marshal(j) - cfg.LoadJSON(tst) - if cfg.ReplicationFactorMin != 3 || cfg.ReplicationFactorMax != 3 { - t.Error("expected replicationFactor Min/Max override") - } + t.Run("default replication factor", func(t *testing.T) { + cfg, err := loadJSON2( + t, + func(j *configJSON) { + j.ReplicationFactorMin = 0 + j.ReplicationFactorMax = 0 + }, + ) + if err != nil { + t.Error(err) + } + if cfg.ReplicationFactorMin != -1 || cfg.ReplicationFactorMax != -1 { + t.Error("expected default replication factors") + } + }) - j = &configJSON{} - json.Unmarshal(ccfgTestJSON, j) - j.ReplicationFactorMin = -1 - tst, _ = json.Marshal(j) - err = cfg.LoadJSON(tst) - if err == nil { - t.Error("expected error when only one replication factor is -1") - } - - j = &configJSON{} - json.Unmarshal(ccfgTestJSON, j) - j.ReplicationFactorMin = 5 - j.ReplicationFactorMax = 4 - tst, _ = json.Marshal(j) - err = cfg.LoadJSON(tst) - if err == nil { - t.Error("expected error when only rplMin > rplMax") - } - - j = &configJSON{} - json.Unmarshal(ccfgTestJSON, j) - j.ReplicationFactorMin = 0 - j.ReplicationFactorMax = 0 - tst, _ = json.Marshal(j) - err = cfg.LoadJSON(tst) - if cfg.ReplicationFactorMin != -1 || cfg.ReplicationFactorMax != -1 { - 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) { diff --git a/package.json b/package.json index 366d7a86..3522d994 100644 --- a/package.json +++ b/package.json @@ -149,6 +149,12 @@ "hash": "QmZMWMvWMVKCbHetJ4RgndbuEF1io2UpUxwQwtNjtYPzSC", "name": "go-ipfs-files", "version": "1.0.1" + }, + { + "author": "lanzafame", + "hash": "QmVPrFJ6dYJyt7uULgE7wouhvXDzYBZVjYvjMCE5Uqxgfy", + "name": "envconfig", + "version": "0.0.0" } ], "gxVersion": "0.11.0",