Skip to content

Commit

Permalink
Merge pull request #749 from iverberk/f-fingerprint-update-node
Browse files Browse the repository at this point in the history
Reregister node when periodic fingerprint changes node properties
  • Loading branch information
dadgar committed Feb 4, 2016
2 parents 6af88ac + a788360 commit 01a221f
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 11 deletions.
75 changes: 64 additions & 11 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/hashicorp/nomad/client/fingerprint"
"github.com/hashicorp/nomad/nomad"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/mitchellh/hashstructure"
)

const (
Expand Down Expand Up @@ -53,6 +54,10 @@ const (
// starting and the intial heartbeat. After the intial heartbeat,
// we switch to using the TTL specified by the servers.
initialHeartbeatStagger = 10 * time.Second

// nodeUpdateRetryIntv is how often the client checks for updates to the
// node attributes or meta map.
nodeUpdateRetryIntv = 30 * time.Second
)

// DefaultConfig returns the default configuration
Expand Down Expand Up @@ -604,17 +609,10 @@ func (c *Client) retryIntv(base time.Duration) time.Duration {

// run is a long lived goroutine used to run the client
func (c *Client) run() {
// Register the client
for {
if err := c.registerNode(); err == nil {
break
}
select {
case <-time.After(c.retryIntv(registerRetryIntv)):
case <-c.shutdownCh:
return
}
}
c.retryRegisterNode()

// Watch for node changes
go c.watchNodeUpdates()

// Setup the heartbeat timer, for the initial registration
// we want to do this quickly. We want to do it extra quickly
Expand Down Expand Up @@ -658,6 +656,42 @@ func (c *Client) run() {
}
}

// hasNodeChanged calculates a hash for the node attributes- and meta map.
// The new hash values are compared against the old (passed-in) hash values to
// determine if the node properties have changed. It returns the new hash values
// in case they are different from the old hash values.
func (c *Client) hasNodeChanged(oldAttrHash uint64, oldMetaHash uint64) (bool, uint64, uint64) {
newAttrHash, err := hashstructure.Hash(c.config.Node.Attributes, nil)
if err != nil {
c.logger.Printf("[DEBUG] client: unable to calculate node attributes hash: %v", err)
}
// Calculate node meta map hash
newMetaHash, err := hashstructure.Hash(c.config.Node.Meta, nil)
if err != nil {
c.logger.Printf("[DEBUG] client: unable to calculate node meta hash: %v", err)
}
if newAttrHash != oldAttrHash || newMetaHash != oldMetaHash {
return true, newAttrHash, newMetaHash
}
return false, oldAttrHash, oldMetaHash
}

// retryRegisterNode is used to register the node or update the registration and
// retry in case of failure.
func (c *Client) retryRegisterNode() {
// Register the client
for {
if err := c.registerNode(); err == nil {
break
}
select {
case <-time.After(c.retryIntv(registerRetryIntv)):
case <-c.shutdownCh:
return
}
}
}

// registerNode is used to register the node or update the registration
func (c *Client) registerNode() error {
node := c.Node()
Expand Down Expand Up @@ -851,6 +885,25 @@ func (c *Client) watchAllocations(updates chan *allocUpdates) {
}
}

// watchNodeUpdates periodically checks for changes to the node attributes or meta map
func (c *Client) watchNodeUpdates() {
c.logger.Printf("[DEBUG] client: periodically checking for node changes at duration %v", nodeUpdateRetryIntv)
var attrHash, metaHash uint64
var changed bool
for {
select {
case <-time.After(nodeUpdateRetryIntv):
changed, attrHash, metaHash = c.hasNodeChanged(attrHash, metaHash)
if changed {
c.logger.Printf("[DEBUG] client: state changed, updating node.")
c.retryRegisterNode()
}
case <-c.shutdownCh:
return
}
}
}

// runAllocs is invoked when we get an updated set of allocations
func (c *Client) runAllocs(update *allocUpdates) {
// Get the existing allocs
Expand Down
32 changes: 32 additions & 0 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/testutil"
"github.com/mitchellh/hashstructure"

ctestutil "github.com/hashicorp/nomad/client/testutil"
)
Expand Down Expand Up @@ -140,6 +141,37 @@ func TestClient_Fingerprint(t *testing.T) {
}
}

func TestClient_HasNodeChanged(t *testing.T) {
c := testClient(t, nil)
defer c.Shutdown()

node := c.Node()
attrHash, err := hashstructure.Hash(node.Attributes, nil)
if err != nil {
c.logger.Printf("[DEBUG] client: unable to calculate node attributes hash: %v", err)
}
// Calculate node meta map hash
metaHash, err := hashstructure.Hash(node.Meta, nil)
if err != nil {
c.logger.Printf("[DEBUG] client: unable to calculate node meta hash: %v", err)
}
if changed, _, _ := c.hasNodeChanged(attrHash, metaHash); changed {
t.Fatalf("Unexpected hash change.")
}

// Change node attribute
node.Attributes["arch"] = "xyz_86"
if changed, newAttrHash, _ := c.hasNodeChanged(attrHash, metaHash); !changed {
t.Fatalf("Expected hash change in attributes: %d vs %d", attrHash, newAttrHash)
}

// Change node meta map
node.Meta["foo"] = "bar"
if changed, _, newMetaHash := c.hasNodeChanged(attrHash, metaHash); !changed {
t.Fatalf("Expected hash change in meta map: %d vs %d", metaHash, newMetaHash)
}
}

func TestClient_Fingerprint_InWhitelist(t *testing.T) {
c := testClient(t, func(c *config.Config) {
if c.Options == nil {
Expand Down

0 comments on commit 01a221f

Please sign in to comment.