284 lines
6.7 KiB
Go
284 lines
6.7 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package structs
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/nomad/ci"
|
|
"github.com/hashicorp/nomad/helper/uuid"
|
|
psstructs "github.com/hashicorp/nomad/plugins/shared/structs"
|
|
"github.com/shoenig/test/must"
|
|
"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",
|
|
NodePool: "dev",
|
|
Status: NodeStatusReady,
|
|
}
|
|
}
|
|
|
|
func TestNode_ComputedClass(t *testing.T) {
|
|
ci.Parallel(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) {
|
|
ci.Parallel(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) {
|
|
ci.Parallel(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) {
|
|
ci.Parallel(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) {
|
|
ci.Parallel(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_ComputedClass_NodePool(t *testing.T) {
|
|
ci.Parallel(t)
|
|
|
|
// Create a node and get its computed class.
|
|
n := testNode()
|
|
err := n.ComputeClass()
|
|
must.NoError(t, err)
|
|
must.NotEq(t, "", n.ComputedClass)
|
|
old := n.ComputedClass
|
|
|
|
// Modify node pool and expect computed class to change.
|
|
n.NodePool = "prod"
|
|
err = n.ComputeClass()
|
|
must.NoError(t, err)
|
|
must.NotEq(t, "", n.ComputedClass)
|
|
must.NotEq(t, old, n.ComputedClass)
|
|
old = n.ComputedClass
|
|
}
|
|
|
|
func TestNode_EscapedConstraints(t *testing.T) {
|
|
ci.Parallel(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)
|
|
}
|
|
}
|