diff --git a/command/agent/agent.go b/command/agent/agent.go index 860d10bd0..f971ff407 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -11,6 +11,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" "github.com/hashicorp/nomad/client" @@ -110,7 +111,7 @@ func (a *Agent) serverConfig() (*nomad.Config, error) { if a.config.Server.BootstrapExpect == 1 { conf.Bootstrap = true } else { - conf.BootstrapExpect = a.config.Server.BootstrapExpect + atomic.StoreInt32(&conf.BootstrapExpect, int32(a.config.Server.BootstrapExpect)) } } if a.config.DataDir != "" { diff --git a/nomad/config.go b/nomad/config.go index d6405c5f6..ff91ac3a2 100644 --- a/nomad/config.go +++ b/nomad/config.go @@ -52,8 +52,9 @@ type Config struct { // BootstrapExpect mode is used to automatically bring up a // collection of Nomad servers. This can be used to automatically - // bring up a collection of nodes. - BootstrapExpect int + // bring up a collection of nodes. All operations on BootstrapExpect + // must be handled via `atomic.*Int32()` calls. + BootstrapExpect int32 // DataDir is the directory to store our state in DataDir string diff --git a/nomad/serf.go b/nomad/serf.go index 9678dbf2a..8f1b6cdaf 100644 --- a/nomad/serf.go +++ b/nomad/serf.go @@ -1,6 +1,10 @@ package nomad -import "github.com/hashicorp/serf/serf" +import ( + "sync/atomic" + + "github.com/hashicorp/serf/serf" +) const ( // StatusReap is used to update the status of a node if we @@ -66,7 +70,7 @@ func (s *Server) nodeJoin(me serf.MemberEvent) { s.peerLock.Unlock() // If we still expecting to bootstrap, may need to handle this - if s.config.BootstrapExpect != 0 { + if atomic.LoadInt32(&s.config.BootstrapExpect) != 0 { s.maybeBootstrap() } } @@ -91,7 +95,7 @@ func (s *Server) maybeBootstrap() { // Bootstrap can only be done if there are no committed logs, // remove our expectations of bootstrapping if index != 0 { - s.config.BootstrapExpect = 0 + atomic.StoreInt32(&s.config.BootstrapExpect, 0) return } @@ -106,7 +110,7 @@ func (s *Server) maybeBootstrap() { if p.Region != s.config.Region { continue } - if p.Expect != 0 && p.Expect != s.config.BootstrapExpect { + if p.Expect != 0 && p.Expect != int(atomic.LoadInt32(&s.config.BootstrapExpect)) { s.logger.Printf("[ERR] nomad: peer %v has a conflicting expect value. All nodes should expect the same number.", member) return } @@ -118,7 +122,7 @@ func (s *Server) maybeBootstrap() { } // Skip if we haven't met the minimum expect count - if len(addrs) < s.config.BootstrapExpect { + if len(addrs) < int(atomic.LoadInt32(&s.config.BootstrapExpect)) { return } @@ -128,8 +132,8 @@ func (s *Server) maybeBootstrap() { s.logger.Printf("[ERR] nomad: failed to bootstrap peers: %v", err) } - // Bootstrapping comlete, don't enter this again - s.config.BootstrapExpect = 0 + // Bootstrapping complete, don't enter this again + atomic.StoreInt32(&s.config.BootstrapExpect, 0) } // nodeFailed is used to handle fail events on the serf cluster diff --git a/nomad/server.go b/nomad/server.go index 5ca922530..3067b0b95 100644 --- a/nomad/server.go +++ b/nomad/server.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" consulapi "github.com/hashicorp/consul/api" @@ -651,8 +652,9 @@ func (s *Server) setupSerf(conf *serf.Config, ch chan serf.Event, path string) ( if s.config.Bootstrap || (s.config.DevMode && !s.config.DevDisableBootstrap) { conf.Tags["bootstrap"] = "1" } - if s.config.BootstrapExpect != 0 { - conf.Tags["expect"] = fmt.Sprintf("%d", s.config.BootstrapExpect) + bootstrapExpect := atomic.LoadInt32(&s.config.BootstrapExpect) + if bootstrapExpect != 0 { + conf.Tags["expect"] = fmt.Sprintf("%d", bootstrapExpect) } conf.MemberlistConfig.LogOutput = s.config.LogOutput conf.LogOutput = s.config.LogOutput