github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/nomad/structs/node_class_test.go (about) 1 package structs 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/hashicorp/nomad/ci" 8 "github.com/hashicorp/nomad/helper/uuid" 9 psstructs "github.com/hashicorp/nomad/plugins/shared/structs" 10 "github.com/stretchr/testify/require" 11 ) 12 13 // TODO Test 14 func testNode() *Node { 15 return &Node{ 16 ID: uuid.Generate(), 17 Datacenter: "dc1", 18 Name: "foobar", 19 Attributes: map[string]string{ 20 "kernel.name": "linux", 21 "arch": "x86", 22 "version": "0.1.0", 23 "driver.exec": "1", 24 }, 25 NodeResources: &NodeResources{ 26 Cpu: NodeCpuResources{ 27 CpuShares: 4000, 28 }, 29 Memory: NodeMemoryResources{ 30 MemoryMB: 8192, 31 }, 32 Disk: NodeDiskResources{ 33 DiskMB: 100 * 1024, 34 }, 35 Networks: []*NetworkResource{ 36 { 37 Device: "eth0", 38 CIDR: "192.168.0.100/32", 39 IP: "192.168.0.100", 40 MBits: 1000, 41 }, 42 }, 43 }, 44 Links: map[string]string{ 45 "consul": "foobar.dc1", 46 }, 47 Meta: map[string]string{ 48 "pci-dss": "true", 49 }, 50 NodeClass: "linux-medium-pci", 51 Status: NodeStatusReady, 52 } 53 } 54 55 func TestNode_ComputedClass(t *testing.T) { 56 ci.Parallel(t) 57 58 require := require.New(t) 59 60 // Create a node and gets it computed class 61 n := testNode() 62 require.NoError(n.ComputeClass()) 63 require.NotEmpty(n.ComputedClass) 64 old := n.ComputedClass 65 66 // Compute again to ensure determinism 67 require.NoError(n.ComputeClass()) 68 require.Equal(n.ComputedClass, old) 69 70 // Modify a field and compute the class again. 71 n.Datacenter = "New DC" 72 require.NoError(n.ComputeClass()) 73 require.NotEqual(n.ComputedClass, old) 74 old = n.ComputedClass 75 76 // Add a device 77 n.NodeResources.Devices = append(n.NodeResources.Devices, &NodeDeviceResource{ 78 Vendor: "foo", 79 Type: "gpu", 80 Name: "bam", 81 }) 82 require.NoError(n.ComputeClass()) 83 require.NotEqual(n.ComputedClass, old) 84 } 85 86 func TestNode_ComputedClass_Ignore(t *testing.T) { 87 ci.Parallel(t) 88 89 require := require.New(t) 90 91 // Create a node and gets it computed class 92 n := testNode() 93 require.NoError(n.ComputeClass()) 94 require.NotEmpty(n.ComputedClass) 95 old := n.ComputedClass 96 97 // Modify an ignored field and compute the class again. 98 n.ID = "New ID" 99 require.NoError(n.ComputeClass()) 100 require.NotEmpty(n.ComputedClass) 101 require.Equal(n.ComputedClass, old) 102 103 } 104 105 func TestNode_ComputedClass_Device_Attr(t *testing.T) { 106 ci.Parallel(t) 107 108 require := require.New(t) 109 110 // Create a node and gets it computed class 111 n := testNode() 112 d := &NodeDeviceResource{ 113 Vendor: "foo", 114 Type: "gpu", 115 Name: "bam", 116 Attributes: map[string]*psstructs.Attribute{ 117 "foo": psstructs.NewBoolAttribute(true), 118 }, 119 } 120 n.NodeResources.Devices = append(n.NodeResources.Devices, d) 121 require.NoError(n.ComputeClass()) 122 require.NotEmpty(n.ComputedClass) 123 old := n.ComputedClass 124 125 // Update the attributes to be have a unique value 126 d.Attributes["unique.bar"] = psstructs.NewBoolAttribute(false) 127 require.NoError(n.ComputeClass()) 128 require.Equal(n.ComputedClass, old) 129 } 130 131 func TestNode_ComputedClass_Attr(t *testing.T) { 132 ci.Parallel(t) 133 134 // Create a node and gets it computed class 135 n := testNode() 136 if err := n.ComputeClass(); err != nil { 137 t.Fatalf("ComputeClass() failed: %v", err) 138 } 139 if n.ComputedClass == "" { 140 t.Fatal("ComputeClass() didn't set computed class") 141 } 142 old := n.ComputedClass 143 144 // Add a unique addr and compute the class again 145 n.Attributes["unique.foo"] = "bar" 146 if err := n.ComputeClass(); err != nil { 147 t.Fatalf("ComputeClass() failed: %v", err) 148 } 149 if old != n.ComputedClass { 150 t.Fatal("ComputeClass() didn't ignore unique attr suffix") 151 } 152 153 // Modify an attribute and compute the class again. 154 n.Attributes["version"] = "New Version" 155 if err := n.ComputeClass(); err != nil { 156 t.Fatalf("ComputeClass() failed: %v", err) 157 } 158 if n.ComputedClass == "" { 159 t.Fatal("ComputeClass() didn't set computed class") 160 } 161 if old == n.ComputedClass { 162 t.Fatal("ComputeClass() ignored attribute change") 163 } 164 165 // Remove and attribute and compute the class again. 166 old = n.ComputedClass 167 delete(n.Attributes, "driver.exec") 168 if err := n.ComputeClass(); err != nil { 169 t.Fatalf("ComputedClass() failed: %v", err) 170 } 171 if n.ComputedClass == "" { 172 t.Fatal("ComputeClass() didn't set computed class") 173 } 174 if old == n.ComputedClass { 175 t.Fatalf("ComputedClass() ignored removal of attribute key") 176 } 177 } 178 179 func TestNode_ComputedClass_Meta(t *testing.T) { 180 ci.Parallel(t) 181 182 // Create a node and gets it computed class 183 n := testNode() 184 if err := n.ComputeClass(); err != nil { 185 t.Fatalf("ComputeClass() failed: %v", err) 186 } 187 if n.ComputedClass == "" { 188 t.Fatal("ComputeClass() didn't set computed class") 189 } 190 old := n.ComputedClass 191 192 // Modify a meta key and compute the class again. 193 n.Meta["pci-dss"] = "false" 194 if err := n.ComputeClass(); err != nil { 195 t.Fatalf("ComputeClass() failed: %v", err) 196 } 197 if n.ComputedClass == "" { 198 t.Fatal("ComputeClass() didn't set computed class") 199 } 200 if old == n.ComputedClass { 201 t.Fatal("ComputeClass() ignored meta change") 202 } 203 old = n.ComputedClass 204 205 // Add a unique meta key and compute the class again. 206 n.Meta["unique.foo"] = "ignore" 207 if err := n.ComputeClass(); err != nil { 208 t.Fatalf("ComputeClass() failed: %v", err) 209 } 210 if n.ComputedClass == "" { 211 t.Fatal("ComputeClass() didn't set computed class") 212 } 213 if old != n.ComputedClass { 214 t.Fatal("ComputeClass() didn't ignore unique meta key") 215 } 216 } 217 218 func TestNode_EscapedConstraints(t *testing.T) { 219 ci.Parallel(t) 220 221 // Non-escaped constraints 222 ne1 := &Constraint{ 223 LTarget: "${attr.kernel.name}", 224 RTarget: "linux", 225 Operand: "=", 226 } 227 ne2 := &Constraint{ 228 LTarget: "${meta.key_foo}", 229 RTarget: "linux", 230 Operand: "<", 231 } 232 ne3 := &Constraint{ 233 LTarget: "${node.dc}", 234 RTarget: "test", 235 Operand: "!=", 236 } 237 238 // Escaped constraints 239 e1 := &Constraint{ 240 LTarget: "${attr.unique.kernel.name}", 241 RTarget: "linux", 242 Operand: "=", 243 } 244 e2 := &Constraint{ 245 LTarget: "${meta.unique.key_foo}", 246 RTarget: "linux", 247 Operand: "<", 248 } 249 e3 := &Constraint{ 250 LTarget: "${unique.node.id}", 251 RTarget: "test", 252 Operand: "!=", 253 } 254 constraints := []*Constraint{ne1, ne2, ne3, e1, e2, e3} 255 expected := []*Constraint{ne1, ne2, ne3} 256 if act := EscapedConstraints(constraints); reflect.DeepEqual(act, expected) { 257 t.Fatalf("EscapedConstraints(%v) returned %v; want %v", constraints, act, expected) 258 } 259 }