diff --git a/config/config.go b/config/config.go index b2355ab80..c2809312a 100644 --- a/config/config.go +++ b/config/config.go @@ -86,9 +86,9 @@ type Config struct { strTrace string `toml:"trace" env:"ETCD_TRACE"` GraphiteHost string `toml:"graphite_host" env:"ETCD_GRAPHITE_HOST"` Cluster struct { - ActiveSize int `toml:"active_size" env:"ETCD_CLUSTER_ACTIVE_SIZE"` - RemoveDelay int `toml:"remove_delay" env:"ETCD_CLUSTER_REMOVE_DELAY"` - SyncInterval int `toml:"sync_interval" env:"ETCD_CLUSTER_SYNC_INTERVAL"` + ActiveSize int `toml:"active_size" env:"ETCD_CLUSTER_ACTIVE_SIZE"` + RemoveDelay float64 `toml:"remove_delay" env:"ETCD_CLUSTER_REMOVE_DELAY"` + SyncInterval float64 `toml:"sync_interval" env:"ETCD_CLUSTER_SYNC_INTERVAL"` } } @@ -207,6 +207,12 @@ func (c *Config) loadEnv(target interface{}) error { value.Field(i).SetString(v) case reflect.Slice: value.Field(i).Set(reflect.ValueOf(ustrings.TrimSplit(v, ","))) + case reflect.Float64: + newValue, err := strconv.ParseFloat(v, 64) + if err != nil { + return fmt.Errorf("Parse error: %s: %s", field.Tag.Get("env"), err) + } + value.Field(i).SetFloat(newValue) } } return nil @@ -265,8 +271,8 @@ func (c *Config) LoadFlags(arguments []string) error { f.StringVar(&c.GraphiteHost, "graphite-host", "", "") f.IntVar(&c.Cluster.ActiveSize, "cluster-active-size", c.Cluster.ActiveSize, "") - f.IntVar(&c.Cluster.RemoveDelay, "cluster-remove-delay", c.Cluster.RemoveDelay, "") - f.IntVar(&c.Cluster.SyncInterval, "cluster-sync-interval", c.Cluster.SyncInterval, "") + f.Float64Var(&c.Cluster.RemoveDelay, "cluster-remove-delay", c.Cluster.RemoveDelay, "") + f.Float64Var(&c.Cluster.SyncInterval, "cluster-sync-interval", c.Cluster.SyncInterval, "") // BEGIN IGNORED FLAGS f.StringVar(&path, "config", "", "") diff --git a/config/config_test.go b/config/config_test.go index c6a022f20..60430819b 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -40,8 +40,8 @@ func TestConfigTOML(t *testing.T) { [cluster] active_size = 5 - remove_delay = 100 - sync_interval = 10 + remove_delay = 100.0 + sync_interval = 10.0 ` c := New() _, err := toml.Decode(content, &c) @@ -68,8 +68,8 @@ func TestConfigTOML(t *testing.T) { assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "") assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:7003", "") assert.Equal(t, c.Cluster.ActiveSize, 5, "") - assert.Equal(t, c.Cluster.RemoveDelay, 100, "") - assert.Equal(t, c.Cluster.SyncInterval, 10, "") + assert.Equal(t, c.Cluster.RemoveDelay, 100.0, "") + assert.Equal(t, c.Cluster.SyncInterval, 10.0, "") } // Ensures that a configuration can be retrieved from environment variables. @@ -123,8 +123,8 @@ func TestConfigEnv(t *testing.T) { assert.Equal(t, c.Peer.KeyFile, "/tmp/peer/file.key", "") assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:7003", "") assert.Equal(t, c.Cluster.ActiveSize, 5, "") - assert.Equal(t, c.Cluster.RemoveDelay, 100, "") - assert.Equal(t, c.Cluster.SyncInterval, 10, "") + assert.Equal(t, c.Cluster.RemoveDelay, 100.0, "") + assert.Equal(t, c.Cluster.SyncInterval, 10.0, "") // Clear this as it will mess up other tests os.Setenv("ETCD_DISCOVERY", "") @@ -484,6 +484,51 @@ func TestConfigPeerBindAddrFlag(t *testing.T) { assert.Equal(t, c.Peer.BindAddr, "127.0.0.1:4003", "") } +// Ensures that the cluster active size can be parsed from the environment. +func TestConfigClusterActiveSizeEnv(t *testing.T) { + withEnv("ETCD_CLUSTER_ACTIVE_SIZE", "5", func(c *Config) { + assert.Nil(t, c.LoadEnv(), "") + assert.Equal(t, c.Cluster.ActiveSize, 5, "") + }) +} + +// Ensures that the cluster active size flag can be parsed. +func TestConfigClusterActiveSizeFlag(t *testing.T) { + c := New() + assert.Nil(t, c.LoadFlags([]string{"-cluster-active-size", "5"}), "") + assert.Equal(t, c.Cluster.ActiveSize, 5, "") +} + +// Ensures that the cluster remove delay can be parsed from the environment. +func TestConfigClusterRemoveDelayEnv(t *testing.T) { + withEnv("ETCD_CLUSTER_REMOVE_DELAY", "100", func(c *Config) { + assert.Nil(t, c.LoadEnv(), "") + assert.Equal(t, c.Cluster.RemoveDelay, 100.0, "") + }) +} + +// Ensures that the cluster remove delay flag can be parsed. +func TestConfigClusterRemoveDelayFlag(t *testing.T) { + c := New() + assert.Nil(t, c.LoadFlags([]string{"-cluster-remove-delay", "100"}), "") + assert.Equal(t, c.Cluster.RemoveDelay, 100.0, "") +} + +// Ensures that the cluster sync interval can be parsed from the environment. +func TestConfigClusterSyncIntervalEnv(t *testing.T) { + withEnv("ETCD_CLUSTER_SYNC_INTERVAL", "10", func(c *Config) { + assert.Nil(t, c.LoadEnv(), "") + assert.Equal(t, c.Cluster.SyncInterval, 10.0, "") + }) +} + +// Ensures that the cluster sync interval flag can be parsed. +func TestConfigClusterSyncIntervalFlag(t *testing.T) { + c := New() + assert.Nil(t, c.LoadFlags([]string{"-cluster-sync-interval", "10"}), "") + assert.Equal(t, c.Cluster.SyncInterval, 10.0, "") +} + // Ensures that a system config field is overridden by a custom config field. func TestConfigCustomConfigOverrideSystemConfig(t *testing.T) { system := `addr = "127.0.0.1:5000"` diff --git a/server/cluster_config.go b/server/cluster_config.go index 44c955fce..2bb191d14 100644 --- a/server/cluster_config.go +++ b/server/cluster_config.go @@ -12,16 +12,16 @@ const ( MinActiveSize = 3 // DefaultRemoveDelay is the default elapsed time before removal. - DefaultRemoveDelay = int((30 * time.Minute) / time.Second) + DefaultRemoveDelay = float64((30 * time.Minute) / time.Second) // MinRemoveDelay is the minimum remove delay allowed. - MinRemoveDelay = int((2 * time.Second) / time.Second) + MinRemoveDelay = float64((2 * time.Second) / time.Second) // DefaultSyncInterval is the default interval for cluster sync. - DefaultSyncInterval = int((30 * time.Minute) / time.Second) + DefaultSyncInterval = float64((30 * time.Minute) / time.Second) // MinSyncInterval is the minimum sync interval allowed. - MinSyncInterval = int((1 * time.Second) / time.Second) + MinSyncInterval = float64((1 * time.Second) / time.Second) ) // ClusterConfig represents cluster-wide configuration settings. @@ -33,11 +33,11 @@ type ClusterConfig struct { // RemoveDelay is the amount of time, in seconds, after a node is // unreachable that it will be swapped out as a standby node. - RemoveDelay int `json:"removeDelay"` + RemoveDelay float64 `json:"removeDelay"` // SyncInterval is the amount of time, in seconds, between // cluster sync when it runs in standby mode. - SyncInterval int `json:"syncInterval"` + SyncInterval float64 `json:"syncInterval"` } // NewClusterConfig returns a cluster configuration with default settings. diff --git a/server/peer_server.go b/server/peer_server.go index d65e1573b..0a317c769 100644 --- a/server/peer_server.go +++ b/server/peer_server.go @@ -817,7 +817,7 @@ func (s *PeerServer) monitorPeerActivity() { // Check last activity for all peers. now := time.Now() - removeDelay := time.Duration(s.ClusterConfig().RemoveDelay) * time.Second + removeDelay := time.Duration(int64(s.ClusterConfig().RemoveDelay * float64(time.Second))) peers := s.raftServer.Peers() for _, peer := range peers { // If the last response from the peer is longer than the remove delay diff --git a/server/peer_server_handlers.go b/server/peer_server_handlers.go index ebeaa182d..b8457126b 100644 --- a/server/peer_server_handlers.go +++ b/server/peer_server_handlers.go @@ -206,10 +206,10 @@ func (ps *PeerServer) setClusterConfigHttpHandler(w http.ResponseWriter, req *ht config.ActiveSize = int(activeSize) } if removeDelay, ok := m["removeDelay"].(float64); ok { - config.RemoveDelay = int(removeDelay) + config.RemoveDelay = removeDelay } if syncInterval, ok := m["syncInterval"].(float64); ok { - config.SyncInterval = int(syncInterval) + config.SyncInterval = syncInterval } // Issue command to update. diff --git a/server/standby_server.go b/server/standby_server.go index 1f849b2db..817030e45 100644 --- a/server/standby_server.go +++ b/server/standby_server.go @@ -119,8 +119,8 @@ func (s *StandbyServer) SyncCluster(peers []string) error { return nil } -func (s *StandbyServer) SetSyncInterval(second int) { - s.syncInterval = time.Duration(second) * time.Second +func (s *StandbyServer) SetSyncInterval(second float64) { + s.syncInterval = time.Duration(int64(second * float64(time.Second))) } func (s *StandbyServer) ClusterLeader() *machineMessage {