open-nomad/nomad/structs/node_class_test.go
Alex Dadgar 4ee603c382 Device hook and devices affect computed node class
This PR introduces a device hook that retrieves the device mount
information for an allocation. It also updates the computed node class
computation to take into account devices.

TODO Fix the task runner unit test. The environment variable is being
lost even though it is being properly set in the prestart hook.
2018-11-27 17:25:33 -08:00

247 lines
6 KiB
Go

package structs
import (
"reflect"
"testing"
"github.com/hashicorp/nomad/helper/uuid"
psstructs "github.com/hashicorp/nomad/plugins/shared/structs"
"github.com/stretchr/testify/require"
)
// TODO Test
func testNode() *Node {
return &Node{
ID: uuid.Generate(),
Datacenter: "dc1",
Name: "foobar",
Attributes: map[string]string{
"kernel.name": "linux",
"arch": "x86",
"version": "0.1.0",
"driver.exec": "1",
},
NodeResources: &NodeResources{
Cpu: NodeCpuResources{
CpuShares: 4000,
},
Memory: NodeMemoryResources{
MemoryMB: 8192,
},
Disk: NodeDiskResources{
DiskMB: 100 * 1024,
},
Networks: []*NetworkResource{
{
Device: "eth0",
CIDR: "192.168.0.100/32",
IP: "192.168.0.100",
MBits: 1000,
},
},
},
Links: map[string]string{
"consul": "foobar.dc1",
},
Meta: map[string]string{
"pci-dss": "true",
},
NodeClass: "linux-medium-pci",
Status: NodeStatusReady,
}
}
func TestNode_ComputedClass(t *testing.T) {
require := require.New(t)
// Create a node and gets it computed class
n := testNode()
require.NoError(n.ComputeClass())
require.NotEmpty(n.ComputedClass)
old := n.ComputedClass
// Compute again to ensure determinism
require.NoError(n.ComputeClass())
require.Equal(n.ComputedClass, old)
// Modify a field and compute the class again.
n.Datacenter = "New DC"
require.NoError(n.ComputeClass())
require.NotEqual(n.ComputedClass, old)
old = n.ComputedClass
// Add a device
n.NodeResources.Devices = append(n.NodeResources.Devices, &NodeDeviceResource{
Vendor: "foo",
Type: "gpu",
Name: "bam",
})
require.NoError(n.ComputeClass())
require.NotEqual(n.ComputedClass, old)
}
func TestNode_ComputedClass_Ignore(t *testing.T) {
require := require.New(t)
// Create a node and gets it computed class
n := testNode()
require.NoError(n.ComputeClass())
require.NotEmpty(n.ComputedClass)
old := n.ComputedClass
// Modify an ignored field and compute the class again.
n.ID = "New ID"
require.NoError(n.ComputeClass())
require.NotEmpty(n.ComputedClass)
require.Equal(n.ComputedClass, old)
}
func TestNode_ComputedClass_Device_Attr(t *testing.T) {
require := require.New(t)
// Create a node and gets it computed class
n := testNode()
d := &NodeDeviceResource{
Vendor: "foo",
Type: "gpu",
Name: "bam",
Attributes: map[string]*psstructs.Attribute{
"foo": psstructs.NewBoolAttribute(true),
},
}
n.NodeResources.Devices = append(n.NodeResources.Devices, d)
require.NoError(n.ComputeClass())
require.NotEmpty(n.ComputedClass)
old := n.ComputedClass
// Update the attributes to be have a unique value
d.Attributes["unique.bar"] = psstructs.NewBoolAttribute(false)
require.NoError(n.ComputeClass())
require.Equal(n.ComputedClass, old)
}
func TestNode_ComputedClass_Attr(t *testing.T) {
// Create a node and gets it computed class
n := testNode()
if err := n.ComputeClass(); err != nil {
t.Fatalf("ComputeClass() failed: %v", err)
}
if n.ComputedClass == "" {
t.Fatal("ComputeClass() didn't set computed class")
}
old := n.ComputedClass
// Add a unique addr and compute the class again
n.Attributes["unique.foo"] = "bar"
if err := n.ComputeClass(); err != nil {
t.Fatalf("ComputeClass() failed: %v", err)
}
if old != n.ComputedClass {
t.Fatal("ComputeClass() didn't ignore unique attr suffix")
}
// Modify an attribute and compute the class again.
n.Attributes["version"] = "New Version"
if err := n.ComputeClass(); err != nil {
t.Fatalf("ComputeClass() failed: %v", err)
}
if n.ComputedClass == "" {
t.Fatal("ComputeClass() didn't set computed class")
}
if old == n.ComputedClass {
t.Fatal("ComputeClass() ignored attribute change")
}
// Remove and attribute and compute the class again.
old = n.ComputedClass
delete(n.Attributes, "driver.exec")
if err := n.ComputeClass(); err != nil {
t.Fatalf("ComputedClass() failed: %v", err)
}
if n.ComputedClass == "" {
t.Fatal("ComputeClass() didn't set computed class")
}
if old == n.ComputedClass {
t.Fatalf("ComputedClass() ignored removal of attribute key")
}
}
func TestNode_ComputedClass_Meta(t *testing.T) {
// Create a node and gets it computed class
n := testNode()
if err := n.ComputeClass(); err != nil {
t.Fatalf("ComputeClass() failed: %v", err)
}
if n.ComputedClass == "" {
t.Fatal("ComputeClass() didn't set computed class")
}
old := n.ComputedClass
// Modify a meta key and compute the class again.
n.Meta["pci-dss"] = "false"
if err := n.ComputeClass(); err != nil {
t.Fatalf("ComputeClass() failed: %v", err)
}
if n.ComputedClass == "" {
t.Fatal("ComputeClass() didn't set computed class")
}
if old == n.ComputedClass {
t.Fatal("ComputeClass() ignored meta change")
}
old = n.ComputedClass
// Add a unique meta key and compute the class again.
n.Meta["unique.foo"] = "ignore"
if err := n.ComputeClass(); err != nil {
t.Fatalf("ComputeClass() failed: %v", err)
}
if n.ComputedClass == "" {
t.Fatal("ComputeClass() didn't set computed class")
}
if old != n.ComputedClass {
t.Fatal("ComputeClass() didn't ignore unique meta key")
}
}
func TestNode_EscapedConstraints(t *testing.T) {
// Non-escaped constraints
ne1 := &Constraint{
LTarget: "${attr.kernel.name}",
RTarget: "linux",
Operand: "=",
}
ne2 := &Constraint{
LTarget: "${meta.key_foo}",
RTarget: "linux",
Operand: "<",
}
ne3 := &Constraint{
LTarget: "${node.dc}",
RTarget: "test",
Operand: "!=",
}
// Escaped constraints
e1 := &Constraint{
LTarget: "${attr.unique.kernel.name}",
RTarget: "linux",
Operand: "=",
}
e2 := &Constraint{
LTarget: "${meta.unique.key_foo}",
RTarget: "linux",
Operand: "<",
}
e3 := &Constraint{
LTarget: "${unique.node.id}",
RTarget: "test",
Operand: "!=",
}
constraints := []*Constraint{ne1, ne2, ne3, e1, e2, e3}
expected := []*Constraint{ne1, ne2, ne3}
if act := EscapedConstraints(constraints); reflect.DeepEqual(act, expected) {
t.Fatalf("EscapedConstraints(%v) returned %v; want %v", constraints, act, expected)
}
}