From eab238f07646c5a3eb2bc5ccf0eb5e9b3b53d801 Mon Sep 17 00:00:00 2001 From: Rob Genova Date: Thu, 12 Apr 2018 16:53:09 +0000 Subject: [PATCH 01/12] New AMI based on bumped dependency versions --- terraform/aws/README.md | 4 ++-- terraform/aws/env/us-east/terraform.tfvars | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/terraform/aws/README.md b/terraform/aws/README.md index d80e71347..d7e1690ec 100644 --- a/terraform/aws/README.md +++ b/terraform/aws/README.md @@ -43,7 +43,7 @@ a custom AMI: ```bash region = "us-east-1" -ami = "ami-3330e54e" +ami = "ami-8b75d9f6" instance_type = "t2.medium" key_name = "KEY_NAME" server_count = "3" @@ -57,7 +57,7 @@ variable like so: ```bash region = "us-east-1" -ami = "ami-3330e54e" +ami = "ami-8b75d9f6" instance_type = "t2.medium" key_name = "KEY_NAME" server_count = "3" diff --git a/terraform/aws/env/us-east/terraform.tfvars b/terraform/aws/env/us-east/terraform.tfvars index 1c0b6aa9e..d62f7b668 100644 --- a/terraform/aws/env/us-east/terraform.tfvars +++ b/terraform/aws/env/us-east/terraform.tfvars @@ -1,5 +1,5 @@ region = "us-east-1" -ami = "ami-3330e54e" +ami = "ami-8b75d9f6" instance_type = "t2.medium" key_name = "KEY_NAME" server_count = "3" From 500c3973667487a3c7dc6d6d59d1f16d7fe637fc Mon Sep 17 00:00:00 2001 From: Rob Genova Date: Sun, 15 Apr 2018 23:00:55 +0000 Subject: [PATCH 02/12] Update to Nomad 0.8, Consul 1.0.7 and Vault 0.10 --- terraform/aws/README.md | 4 ++-- terraform/aws/env/us-east/terraform.tfvars | 2 +- terraform/shared/scripts/setup.sh | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/terraform/aws/README.md b/terraform/aws/README.md index d7e1690ec..30b1523ce 100644 --- a/terraform/aws/README.md +++ b/terraform/aws/README.md @@ -43,7 +43,7 @@ a custom AMI: ```bash region = "us-east-1" -ami = "ami-8b75d9f6" +ami = "ami-d4b016ab" instance_type = "t2.medium" key_name = "KEY_NAME" server_count = "3" @@ -57,7 +57,7 @@ variable like so: ```bash region = "us-east-1" -ami = "ami-8b75d9f6" +ami = "ami-d4b016ab" instance_type = "t2.medium" key_name = "KEY_NAME" server_count = "3" diff --git a/terraform/aws/env/us-east/terraform.tfvars b/terraform/aws/env/us-east/terraform.tfvars index d62f7b668..013c11c02 100644 --- a/terraform/aws/env/us-east/terraform.tfvars +++ b/terraform/aws/env/us-east/terraform.tfvars @@ -1,5 +1,5 @@ region = "us-east-1" -ami = "ami-8b75d9f6" +ami = "ami-d4b016ab" instance_type = "t2.medium" key_name = "KEY_NAME" server_count = "3" diff --git a/terraform/shared/scripts/setup.sh b/terraform/shared/scripts/setup.sh index 078817693..594f38874 100644 --- a/terraform/shared/scripts/setup.sh +++ b/terraform/shared/scripts/setup.sh @@ -9,17 +9,17 @@ cd /ops CONFIGDIR=/ops/shared/config -CONSULVERSION=1.0.6 +CONSULVERSION=1.0.7 CONSULDOWNLOAD=https://releases.hashicorp.com/consul/${CONSULVERSION}/consul_${CONSULVERSION}_linux_amd64.zip CONSULCONFIGDIR=/etc/consul.d CONSULDIR=/opt/consul -VAULTVERSION=0.9.6 +VAULTVERSION=0.10.0 VAULTDOWNLOAD=https://releases.hashicorp.com/vault/${VAULTVERSION}/vault_${VAULTVERSION}_linux_amd64.zip VAULTCONFIGDIR=/etc/vault.d VAULTDIR=/opt/vault -NOMADVERSION=0.7.1 +NOMADVERSION=0.8.0 NOMADDOWNLOAD=https://releases.hashicorp.com/nomad/${NOMADVERSION}/nomad_${NOMADVERSION}_linux_amd64.zip NOMADCONFIGDIR=/etc/nomad.d NOMADDIR=/opt/nomad From da8bd05d366eeead5a0c4df5db7082c0c9f8540b Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Mon, 16 Apr 2018 15:03:33 +0800 Subject: [PATCH 03/12] Fix consul download url in Vagrant demo This issue was introduced in 07cbe23b8c5bc23a4290a577909f1731de81dc7c ``` $ curl -I https://releases.hashicorp.com/consul/1.0.0/consul_1.0.7_linux_amd64.zip curl: (22) The requested URL returned error: 403 Forbidden $ curl -I https://releases.hashicorp.com/consul/1.0.7/consul_1.0.7_linux_amd64.zip HTTP/1.1 200 OK Cache-Control: max-age=31536000, stale-white-revalidate=86400, stale-if-error=604800, public Content-Disposition: attachment Last-Modified: Fri, 13 Apr 2018 17:21:08 GMT ETag: "c5ed9baed01b2b3e52a7ecb9850cecc6" Content-Type: application/zip Strict-Transport-Security: max-age=31536000; includeSubDomains; preload X-XSS-Protection: 1; mode=block X-Content-Type-Options: nosniff X-Frame-Options: sameorigin Content-Length: 12317246 Accept-Ranges: bytes Date: Mon, 16 Apr 2018 07:04:47 GMT Connection: keep-alive ``` --- demo/vagrant/Vagrantfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/vagrant/Vagrantfile b/demo/vagrant/Vagrantfile index 39cd663ec..dba631e6e 100644 --- a/demo/vagrant/Vagrantfile +++ b/demo/vagrant/Vagrantfile @@ -17,7 +17,7 @@ cd /tmp/ curl -sSL https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_amd64.zip -o nomad.zip echo "Fetching Consul..." -curl -sSL https://releases.hashicorp.com/consul/1.0.0/consul_1.0.7_linux_amd64.zip > consul.zip +curl -sSL https://releases.hashicorp.com/consul/1.0.7/consul_1.0.7_linux_amd64.zip > consul.zip echo "Installing Nomad..." unzip nomad.zip From dfc28f4813dadac5efa13f581f62f0fa0e104eae Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Mon, 16 Apr 2018 15:11:20 +0800 Subject: [PATCH 04/12] Improve CPU core detection in Vagrantfile Use single `grep` with `^` condition instead of `grep` + `cat` + `wc` --- Vagrantfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Vagrantfile b/Vagrantfile index b28495648..46d6882e5 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -156,7 +156,7 @@ def suggestedCPUCores() when /darwin/ Integer(`sysctl -n hw.ncpu`) / 2 when /linux/ - Integer(`cat /proc/cpuinfo | grep processor | wc -l`) / 2 + Integer(`grep -c ^processor /proc/cpuinfo`) / 2 else 2 end From bb4d7be2d0a5b29b8c9a3f4c0e960b94a2e27858 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Mon, 16 Apr 2018 15:29:37 +0800 Subject: [PATCH 05/12] [Docs] Update output message of `nomad node status` --- website/source/docs/commands/node/status.html.md.erb | 12 ++++++------ website/source/guides/securing-nomad.html.md | 4 ++-- website/source/intro/getting-started/cluster.html.md | 6 +++--- website/source/intro/getting-started/running.html.md | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/website/source/docs/commands/node/status.html.md.erb b/website/source/docs/commands/node/status.html.md.erb index 91c94b9a1..f3713e045 100644 --- a/website/source/docs/commands/node/status.html.md.erb +++ b/website/source/docs/commands/node/status.html.md.erb @@ -56,18 +56,18 @@ List view: ``` $ nomad node status -ID DC Name Drain Status -a72dfba2 dc1 node1 false ready -1f3f03ea dc1 node2 false ready +ID DC Name Class Drain Eligibility Status +a72dfba2 dc1 node1 false eligible ready +1f3f03ea dc1 node2 false eligible ready ``` List view, with running allocations: ``` $ nomad node status -allocs -ID DC Name Class Drain Status Running Allocs -4d2ba53b dc1 node1 false ready 1 -34dfba32 dc1 node2 false ready 3 +ID DC Name Class Drain Eligibility Status Running Allocs +4d2ba53b dc1 node1 false eligible ready 1 +34dfba32 dc1 node2 false eligible ready 3 ``` Single-node view in short mode: diff --git a/website/source/guides/securing-nomad.html.md b/website/source/guides/securing-nomad.html.md index c5ab6b937..98fdd1a2f 100644 --- a/website/source/guides/securing-nomad.html.md +++ b/website/source/guides/securing-nomad.html.md @@ -326,8 +326,8 @@ respond as expected: ```text $ nomad node status -ID DC Name Class Drain Status -237cd4c5 dc1 nomad false ready +ID DC Name Class Drain Eligibility Status +237cd4c5 dc1 nomad false eligible ready $ nomad job init Example job file written to example.nomad diff --git a/website/source/intro/getting-started/cluster.html.md b/website/source/intro/getting-started/cluster.html.md index 5ffc2fd62..7ba2e0c51 100644 --- a/website/source/intro/getting-started/cluster.html.md +++ b/website/source/intro/getting-started/cluster.html.md @@ -142,9 +142,9 @@ we should see both nodes in the `ready` state: ```text $ nomad node status -ID Datacenter Name Class Drain Status -fca62612 dc1 client1 false ready -c887deef dc1 client2 false ready +ID DC Name Class Drain Eligibility Status +fca62612 dc1 client1 false eligible ready +c887deef dc1 client2 false eligible ready ``` We now have a simple three node cluster running. The only difference diff --git a/website/source/intro/getting-started/running.html.md b/website/source/intro/getting-started/running.html.md index 49e498fcc..2a66ef91d 100644 --- a/website/source/intro/getting-started/running.html.md +++ b/website/source/intro/getting-started/running.html.md @@ -82,8 +82,8 @@ $ vagrant ssh ... $ nomad node status -ID Datacenter Name Class Drain Status -171a583b dc1 nomad false ready +ID DC Name Class Drain Eligibility Status +171a583b dc1 nomad false eligible ready ``` The output shows our Node ID, which is a randomly generated UUID, From 9929019b9afe6562f7521288bf0954e971975986 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Apr 2018 13:28:23 -0700 Subject: [PATCH 06/12] Operate on copy --- client/client.go | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/client/client.go b/client/client.go index 46d8bd1d7..cfefef1f6 100644 --- a/client/client.go +++ b/client/client.go @@ -259,6 +259,12 @@ func NewClient(cfg *config.Config, consulCatalog consul.CatalogAPI, consulServic return nil, fmt.Errorf("node setup failed: %v", err) } + // Store the config copy before restoring state but after it has been + // initialized. + c.configLock.Lock() + c.configCopy = c.config.Copy() + c.configLock.Unlock() + fingerprintManager := NewFingerprintManager(c.GetConfig, c.config.Node, c.shutdownCh, c.updateNodeFromFingerprint, c.updateNodeFromDriver, c.logger) @@ -1009,10 +1015,10 @@ func (c *Client) updateNodeFromFingerprint(response *cstructs.FingerprintRespons } if nodeHasChanged { - c.updateNode() + c.updateNodeLocked() } - return c.config.Node + return c.configCopy.Node } // updateNodeFromDriver receives either a fingerprint of the driver or its @@ -1104,10 +1110,10 @@ func (c *Client) updateNodeFromDriver(name string, fingerprint, health *structs. if hasChanged { c.config.Node.Drivers[name].UpdateTime = time.Now() - c.updateNode() + c.updateNodeLocked() } - return c.config.Node + return c.configCopy.Node } // resourcesAreEqual is a temporary function to compare whether resources are @@ -1752,9 +1758,14 @@ OUTER: } } -// updateNode triggers a client to update its node copy if it isn't doing +// updateNode updates the Node copy and triggers the client to send the updated +// Node to the server. This should be done while holding the configLock lock. // so already -func (c *Client) updateNode() { +func (c *Client) updateNodeLocked() { + // Update the config copy. + node := c.config.Node.Copy() + c.configCopy.Node = node + select { case c.triggerNodeUpdate <- struct{}{}: // Node update goroutine was released to execute @@ -1774,15 +1785,7 @@ func (c *Client) watchNodeUpdates() { select { case <-timer.C: c.logger.Printf("[DEBUG] client: state changed, updating node and re-registering.") - - // Update the config copy. - c.configLock.Lock() - node := c.config.Node.Copy() - c.configCopy.Node = node - c.configLock.Unlock() - c.retryRegisterNode() - hasChanged = false case <-c.triggerNodeUpdate: if hasChanged { From 89fa9a1e10c064ebc72fc76252e72d6ea2cb399c Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Apr 2018 15:02:00 -0700 Subject: [PATCH 07/12] Fix copying drivers --- client/client.go | 4 ++-- nomad/structs/node.go | 13 +++++++++++++ nomad/structs/structs.go | 15 +++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/client/client.go b/client/client.go index cfefef1f6..2cb9dac59 100644 --- a/client/client.go +++ b/client/client.go @@ -265,7 +265,7 @@ func NewClient(cfg *config.Config, consulCatalog consul.CatalogAPI, consulServic c.configCopy = c.config.Copy() c.configLock.Unlock() - fingerprintManager := NewFingerprintManager(c.GetConfig, c.config.Node, + fingerprintManager := NewFingerprintManager(c.GetConfig, c.configCopy.Node, c.shutdownCh, c.updateNodeFromFingerprint, c.updateNodeFromDriver, c.logger) @@ -443,7 +443,7 @@ func (c *Client) Leave() error { func (c *Client) GetConfig() *config.Config { c.configLock.Lock() defer c.configLock.Unlock() - return c.config + return c.configCopy } // Datacenter returns the datacenter for the given client diff --git a/nomad/structs/node.go b/nomad/structs/node.go index a4eb91e71..76758fb8e 100644 --- a/nomad/structs/node.go +++ b/nomad/structs/node.go @@ -2,6 +2,8 @@ package structs import ( "time" + + "github.com/hashicorp/nomad/helper" ) // DriverInfo is the current state of a single driver. This is updated @@ -14,6 +16,17 @@ type DriverInfo struct { UpdateTime time.Time } +func (di *DriverInfo) Copy() *DriverInfo { + if di == nil { + return nil + } + + cdi := new(DriverInfo) + *cdi = *di + cdi.Attributes = helper.CopyMapStringString(di.Attributes) + return cdi +} + // MergeHealthCheck merges information from a health check for a drier into a // node's driver info func (di *DriverInfo) MergeHealthCheck(other *DriverInfo) { diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 1fd30e322..60d8f0d80 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -1461,6 +1461,7 @@ func (n *Node) Copy() *Node { nn.Meta = helper.CopyMapStringString(nn.Meta) nn.Events = copyNodeEvents(n.Events) nn.DrainStrategy = nn.DrainStrategy.Copy() + nn.Drivers = copyNodeDrivers(n.Drivers) return nn } @@ -1478,6 +1479,20 @@ func copyNodeEvents(events []*NodeEvent) []*NodeEvent { return c } +// copyNodeDrivers is a helper to copy a map of DriverInfo +func copyNodeDrivers(drivers map[string]*DriverInfo) map[string]*DriverInfo { + l := len(drivers) + if l == 0 { + return nil + } + + c := make(map[string]*DriverInfo, l) + for driver, info := range drivers { + c[driver] = info.Copy() + } + return c +} + // TerminalStatus returns if the current status is terminal and // will no longer transition. func (n *Node) TerminalStatus() bool { From 741975657db3663da4af49645bcf7007f9adc921 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Apr 2018 15:07:08 -0700 Subject: [PATCH 08/12] fix race node access --- client/fingerprint_manager.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/fingerprint_manager.go b/client/fingerprint_manager.go index 499cc16d7..cdd9e1472 100644 --- a/client/fingerprint_manager.go +++ b/client/fingerprint_manager.go @@ -53,10 +53,16 @@ func NewFingerprintManager(getConfig func() *config.Config, func (fm *FingerprintManager) setNode(node *structs.Node) { fm.nodeLock.Lock() defer fm.nodeLock.Unlock() - fm.node = node } +// getNode returns the current client node +func (fm *FingerprintManager) getNode() *structs.Node { + fm.nodeLock.Lock() + defer fm.nodeLock.Unlock() + return fm.node +} + // Run starts the process of fingerprinting the node. It does an initial pass, // identifying whitelisted and blacklisted fingerprints/drivers. Then, for // those which require periotic checking, it starts a periodic process for @@ -167,7 +173,7 @@ func (fm *FingerprintManager) setupFingerprinters(fingerprints []string) error { // supported func (fm *FingerprintManager) setupDrivers(drivers []string) error { var availDrivers []string - driverCtx := driver.NewDriverContext("", "", fm.getConfig(), fm.node, fm.logger, nil) + driverCtx := driver.NewDriverContext("", "", fm.getConfig(), fm.getNode(), fm.logger, nil) for _, name := range drivers { d, err := driver.NewDriver(name, driverCtx) From 3cf87e0dc8ce19820545054c5cd33bc5a043cb69 Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Apr 2018 15:41:32 -0700 Subject: [PATCH 09/12] Copy the config given to the alloc runner --- client/client.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/client.go b/client/client.go index 2cb9dac59..82e685f12 100644 --- a/client/client.go +++ b/client/client.go @@ -732,7 +732,7 @@ func (c *Client) restoreState() error { watcher := noopPrevAlloc{} c.configLock.RLock() - ar := NewAllocRunner(c.logger, c.configCopy, c.stateDB, c.updateAllocStatus, alloc, c.vaultClient, c.consulService, watcher) + ar := NewAllocRunner(c.logger, c.configCopy.Copy(), c.stateDB, c.updateAllocStatus, alloc, c.vaultClient, c.consulService, watcher) c.configLock.RUnlock() c.allocLock.Lock() @@ -1902,7 +1902,10 @@ func (c *Client) addAlloc(alloc *structs.Allocation, migrateToken string) error c.configLock.RLock() prevAlloc := newAllocWatcher(alloc, prevAR, c, c.configCopy, c.logger, migrateToken) - ar := NewAllocRunner(c.logger, c.configCopy, c.stateDB, c.updateAllocStatus, alloc, c.vaultClient, c.consulService, prevAlloc) + // Copy the config since the node can be swapped out as it is being updated. + // The long term fix is to pass in the config and node separately and then + // we don't have to do a copy. + ar := NewAllocRunner(c.logger, c.configCopy.Copy(), c.stateDB, c.updateAllocStatus, alloc, c.vaultClient, c.consulService, prevAlloc) c.configLock.RUnlock() // Store the alloc runner. From cc3f264a35f4f1e8826d7de0d8917968cd34ed9b Mon Sep 17 00:00:00 2001 From: Alex Dadgar Date: Mon, 16 Apr 2018 15:48:34 -0700 Subject: [PATCH 10/12] Cleanup --- client/client.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/client/client.go b/client/client.go index 82e685f12..35adbc534 100644 --- a/client/client.go +++ b/client/client.go @@ -277,12 +277,6 @@ func NewClient(cfg *config.Config, consulCatalog consul.CatalogAPI, consulServic // Setup the reserved resources c.reservePorts() - // Store the config copy before restoring state but after it has been - // initialized. - c.configLock.Lock() - c.configCopy = c.config.Copy() - c.configLock.Unlock() - // Set the preconfigured list of static servers c.configLock.RLock() if len(c.configCopy.Servers) > 0 { @@ -969,6 +963,9 @@ func (c *Client) reservePorts() { for _, net := range reservedIndex { node.Reserved.Networks = append(node.Reserved.Networks, net) } + + // Make the changes available to the config copy. + c.configCopy = c.config.Copy() } // updateNodeFromFingerprint updates the node with the result of From 3ebecb676cbad9f0f7e1b7fe8156d04336ccb971 Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Tue, 17 Apr 2018 11:53:08 -0400 Subject: [PATCH 11/12] fix up comments --- client/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/client.go b/client/client.go index 35adbc534..a3e8dad6b 100644 --- a/client/client.go +++ b/client/client.go @@ -1756,8 +1756,8 @@ OUTER: } // updateNode updates the Node copy and triggers the client to send the updated -// Node to the server. This should be done while holding the configLock lock. -// so already +// Node to the server. This should be done while the caller holds the +// configLock lock. func (c *Client) updateNodeLocked() { // Update the config copy. node := c.config.Node.Copy() From 0596828eb5499b226241dc0f0063562d0547ba1b Mon Sep 17 00:00:00 2001 From: Chelsea Holland Komlo Date: Tue, 17 Apr 2018 11:53:18 -0400 Subject: [PATCH 12/12] add test for node copy --- nomad/structs/structs_test.go | 77 +++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/nomad/structs/structs_test.go b/nomad/structs/structs_test.go index 8883e7a0b..540441d61 100644 --- a/nomad/structs/structs_test.go +++ b/nomad/structs/structs_test.go @@ -3676,3 +3676,80 @@ func TestNode_Canonicalize(t *testing.T) { node.Canonicalize() require.Equal(NodeSchedulingIneligible, node.SchedulingEligibility) } + +func TestNode_Copy(t *testing.T) { + t.Parallel() + require := require.New(t) + + node := &Node{ + ID: uuid.Generate(), + SecretID: uuid.Generate(), + Datacenter: "dc1", + Name: "foobar", + Attributes: map[string]string{ + "kernel.name": "linux", + "arch": "x86", + "nomad.version": "0.5.0", + "driver.exec": "1", + "driver.mock_driver": "1", + }, + Resources: &Resources{ + CPU: 4000, + MemoryMB: 8192, + DiskMB: 100 * 1024, + IOPS: 150, + Networks: []*NetworkResource{ + { + Device: "eth0", + CIDR: "192.168.0.100/32", + MBits: 1000, + }, + }, + }, + Reserved: &Resources{ + CPU: 100, + MemoryMB: 256, + DiskMB: 4 * 1024, + Networks: []*NetworkResource{ + { + Device: "eth0", + IP: "192.168.0.100", + ReservedPorts: []Port{{Label: "ssh", Value: 22}}, + MBits: 1, + }, + }, + }, + Links: map[string]string{ + "consul": "foobar.dc1", + }, + Meta: map[string]string{ + "pci-dss": "true", + "database": "mysql", + "version": "5.6", + }, + NodeClass: "linux-medium-pci", + Status: NodeStatusReady, + SchedulingEligibility: NodeSchedulingEligible, + Drivers: map[string]*DriverInfo{ + "mock_driver": &DriverInfo{ + Attributes: map[string]string{"running": "1"}, + Detected: true, + Healthy: true, + HealthDescription: "Currently active", + UpdateTime: time.Now(), + }, + }, + } + node.ComputeClass() + + node2 := node.Copy() + + require.Equal(node.Attributes, node2.Attributes) + require.Equal(node.Resources, node2.Resources) + require.Equal(node.Reserved, node2.Reserved) + require.Equal(node.Links, node2.Links) + require.Equal(node.Meta, node2.Meta) + require.Equal(node.Events, node2.Events) + require.Equal(node.DrainStrategy, node2.DrainStrategy) + require.Equal(node.Drivers, node2.Drivers) +}