github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/scheduler/constraint_test.go (about) 1 package scheduler 2 3 import ( 4 "testing" 5 6 "github.com/docker/swarmkit/api" 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 ) 10 11 var ( 12 task1 *api.Task 13 ni *NodeInfo 14 ) 15 16 func setupEnv() { 17 task1 = &api.Task{ 18 ID: "id1", 19 DesiredState: api.TaskStateRunning, 20 ServiceAnnotations: api.Annotations{ 21 Name: "name1", 22 }, 23 24 Spec: api.TaskSpec{ 25 Runtime: &api.TaskSpec_Container{ 26 Container: &api.ContainerSpec{ 27 Command: []string{"sh", "-c", "sleep 5"}, 28 Image: "alpine", 29 }, 30 }, 31 }, 32 33 Status: api.TaskStatus{ 34 State: api.TaskStateAssigned, 35 }, 36 } 37 38 ni = &NodeInfo{ 39 Node: &api.Node{ 40 ID: "nodeid-1", 41 Spec: api.NodeSpec{ 42 Annotations: api.Annotations{ 43 Labels: make(map[string]string), 44 }, 45 DesiredRole: api.NodeRoleWorker, 46 }, 47 Description: &api.NodeDescription{ 48 Engine: &api.EngineDescription{ 49 Labels: make(map[string]string), 50 }, 51 }, 52 Status: api.NodeStatus{ 53 State: api.NodeStatus_READY, 54 Addr: "186.17.9.41", 55 }, 56 }, 57 Tasks: make(map[string]*api.Task), 58 ActiveTasksCountByService: make(map[string]int), 59 } 60 } 61 62 func TestConstraintSetTask(t *testing.T) { 63 setupEnv() 64 f := ConstraintFilter{} 65 assert.False(t, f.SetTask(task1)) 66 67 task1.Spec.Placement = &api.Placement{ 68 Constraints: []string{"node.hostname == node-2", "node.labels.security != low"}, 69 } 70 assert.True(t, f.SetTask(task1)) 71 72 task1.Spec.Placement = &api.Placement{ 73 Constraints: []string{"node.id == nodeid-2", "engine.labels.operatingsystem != ubuntu"}, 74 } 75 assert.True(t, f.SetTask(task1)) 76 } 77 78 func TestWrongSyntax(t *testing.T) { 79 setupEnv() 80 f := ConstraintFilter{} 81 task1.Spec.Placement = &api.Placement{ 82 Constraints: []string{"node.abc.bcd == high"}, 83 } 84 require.True(t, f.SetTask(task1)) 85 assert.False(t, f.Check(ni)) 86 87 task1.Spec.Placement = &api.Placement{ 88 Constraints: []string{"node.abc.bcd != high"}, 89 } 90 require.True(t, f.SetTask(task1)) 91 assert.False(t, f.Check(ni)) 92 } 93 94 func TestNodeHostname(t *testing.T) { 95 setupEnv() 96 f := ConstraintFilter{} 97 task1.Spec.Placement = &api.Placement{ 98 Constraints: []string{"node.hostname != node-1"}, 99 } 100 require.True(t, f.SetTask(task1)) 101 102 // the node without hostname passes constraint 103 assert.True(t, f.Check(ni)) 104 105 // add a not matching hostname 106 ni.Description.Hostname = "node-2" 107 assert.True(t, f.Check(ni)) 108 109 // matching engine name 110 ni.Description.Hostname = "node-1" 111 assert.False(t, f.Check(ni)) 112 113 // case insensitive 114 ni.Node.Description.Hostname = "NODe-1" 115 assert.False(t, f.Check(ni)) 116 } 117 118 func TestNodeIP(t *testing.T) { 119 setupEnv() 120 f := ConstraintFilter{} 121 122 type testcase struct { 123 constraints []string 124 requireVerdict bool 125 assertVerdict bool 126 } 127 128 testFunc := func(tc testcase) { 129 task1.Spec.Placement = &api.Placement{ 130 Constraints: tc.constraints, 131 } 132 require.Equal(t, f.SetTask(task1), tc.requireVerdict) 133 if tc.requireVerdict { 134 assert.Equal(t, f.Check(ni), tc.assertVerdict) 135 } 136 } 137 138 ipv4tests := []testcase{ 139 {[]string{"node.ip == 186.17.9.41"}, true, true}, 140 {[]string{"node.ip != 186.17.9.41"}, true, false}, 141 {[]string{"node.ip == 186.17.9.42"}, true, false}, 142 {[]string{"node.ip == 186.17.9.4/24"}, true, true}, 143 {[]string{"node.ip == 186.17.8.41/24"}, true, false}, 144 // invalid CIDR format 145 {[]string{"node.ip == 186.17.9.41/34"}, true, false}, 146 // malformed IP 147 {[]string{"node.ip != 266.17.9.41"}, true, false}, 148 // zero 149 {[]string{"node.ip != 0.0.0.0"}, true, true}, 150 // invalid input, detected by SetTask 151 {[]string{"node.ip == "}, false, true}, 152 // invalid input, not detected by SetTask 153 {[]string{"node.ip == not_ip_addr"}, true, false}, 154 } 155 156 for _, tc := range ipv4tests { 157 testFunc(tc) 158 } 159 160 // IPv6 address 161 ni.Status.Addr = "2001:db8::2" 162 ipv6tests := []testcase{ 163 {[]string{"node.ip == 2001:db8::2"}, true, true}, 164 // same IPv6 address, different format 165 {[]string{"node.ip == 2001:db8:0::2"}, true, true}, 166 {[]string{"node.ip != 2001:db8::2/128"}, true, false}, 167 {[]string{"node.ip == 2001:db8::/64"}, true, true}, 168 {[]string{"node.ip == 2001:db9::/64"}, true, false}, 169 {[]string{"node.ip != 2001:db9::/64"}, true, true}, 170 } 171 172 for _, tc := range ipv6tests { 173 testFunc(tc) 174 } 175 176 // node doesn't have address 177 ni.Status.Addr = "" 178 edgetests := []testcase{ 179 {[]string{"node.ip == 0.0.0.0"}, true, false}, 180 {[]string{"node.ip != 0.0.0.0"}, true, true}, 181 } 182 183 for _, tc := range edgetests { 184 testFunc(tc) 185 } 186 } 187 188 func TestNodeID(t *testing.T) { 189 setupEnv() 190 f := ConstraintFilter{} 191 task1.Spec.Placement = &api.Placement{ 192 Constraints: []string{"node.id == nodeid-1"}, 193 } 194 require.True(t, f.SetTask(task1)) 195 assert.True(t, f.Check(ni)) 196 197 // full text match, cannot be longer 198 task1.Spec.Placement = &api.Placement{ 199 Constraints: []string{"node.id == nodeid-1-extra"}, 200 } 201 require.True(t, f.SetTask(task1)) 202 assert.False(t, f.Check(ni)) 203 204 // cannot be shorter 205 task1.Spec.Placement = &api.Placement{ 206 Constraints: []string{"node.id == nodeid-"}, 207 } 208 require.True(t, f.SetTask(task1)) 209 assert.False(t, f.Check(ni)) 210 } 211 212 func TestNodeRole(t *testing.T) { 213 setupEnv() 214 f := ConstraintFilter{} 215 task1.Spec.Placement = &api.Placement{ 216 Constraints: []string{"node.role == worker"}, 217 } 218 require.True(t, f.SetTask(task1)) 219 assert.True(t, f.Check(ni)) 220 221 task1.Spec.Placement = &api.Placement{ 222 Constraints: []string{"node.role == manager"}, 223 } 224 require.True(t, f.SetTask(task1)) 225 assert.False(t, f.Check(ni)) 226 227 // no such role as worker-manage 228 task1.Spec.Placement = &api.Placement{ 229 Constraints: []string{"node.role == worker-manager"}, 230 } 231 require.True(t, f.SetTask(task1)) 232 assert.False(t, f.Check(ni)) 233 } 234 235 func TestNodePlatform(t *testing.T) { 236 setupEnv() 237 f := ConstraintFilter{} 238 task1.Spec.Placement = &api.Placement{ 239 Constraints: []string{"node.platform.os == linux"}, 240 } 241 require.True(t, f.SetTask(task1)) 242 //node info doesn't have platform yet 243 assert.False(t, f.Check(ni)) 244 245 ni.Node.Description.Platform = &api.Platform{ 246 Architecture: "x86_64", 247 OS: "linux", 248 } 249 assert.True(t, f.Check(ni)) 250 251 ni.Node.Description.Platform = &api.Platform{ 252 Architecture: "x86_64", 253 OS: "windows", 254 } 255 assert.False(t, f.Check(ni)) 256 257 task1.Spec.Placement = &api.Placement{ 258 Constraints: []string{"node.platform.arch == amd64"}, 259 } 260 require.True(t, f.SetTask(task1)) 261 assert.False(t, f.Check(ni)) 262 263 task1.Spec.Placement = &api.Placement{ 264 Constraints: []string{"node.platform.arch != amd64"}, 265 } 266 require.True(t, f.SetTask(task1)) 267 assert.True(t, f.Check(ni)) 268 } 269 270 func TestNodeLabel(t *testing.T) { 271 setupEnv() 272 f := ConstraintFilter{} 273 task1.Spec.Placement = &api.Placement{ 274 Constraints: []string{"node.labels.security == high"}, 275 } 276 require.True(t, f.SetTask(task1)) 277 assert.False(t, f.Check(ni)) 278 279 // engine label is not node label 280 ni.Description.Engine.Labels["security"] = "high" 281 assert.False(t, f.Check(ni)) 282 283 ni.Spec.Annotations.Labels["security"] = "high" 284 assert.True(t, f.Check(ni)) 285 } 286 287 func TestEngineLabel(t *testing.T) { 288 setupEnv() 289 f := ConstraintFilter{} 290 task1.Spec.Placement = &api.Placement{ 291 Constraints: []string{"engine.labels.disk != ssd"}, 292 } 293 require.True(t, f.SetTask(task1)) 294 // no such label matches != 295 assert.True(t, f.Check(ni)) 296 297 // node label is not engine label 298 ni.Spec.Annotations.Labels["disk"] = "ssd" 299 assert.True(t, f.Check(ni)) 300 301 ni.Description.Engine.Labels["disk"] = "ssd" 302 assert.False(t, f.Check(ni)) 303 304 // extra label doesn't interfere 305 ni.Description.Engine.Labels["memory"] = "large" 306 assert.False(t, f.Check(ni)) 307 } 308 309 func TestMultipleConstraints(t *testing.T) { 310 setupEnv() 311 f := ConstraintFilter{} 312 task1.Spec.Placement = &api.Placement{ 313 Constraints: []string{"node.hostname == node-1", "engine.labels.operatingsystem != Ubuntu 14.04"}, 314 } 315 require.True(t, f.SetTask(task1)) 316 assert.False(t, f.Check(ni)) 317 318 ni.Description.Hostname = "node-1" 319 assert.True(t, f.Check(ni)) 320 321 // set node operating system 322 ni.Node.Description.Engine.Labels["operatingsystem"] = "Ubuntu 14.04" 323 assert.False(t, f.Check(ni)) 324 325 // case insensitive 326 ni.Node.Description.Engine.Labels["operatingsystem"] = "ubuntu 14.04" 327 assert.False(t, f.Check(ni)) 328 329 ni.Node.Description.Engine.Labels["operatingsystem"] = "ubuntu 15.04" 330 assert.True(t, f.Check(ni)) 331 332 // add one more label requirement to task 333 task1.Spec.Placement = &api.Placement{ 334 Constraints: []string{"node.hostname == node-1", 335 "engine.labels.operatingsystem != Ubuntu 14.04", 336 "node.labels.security == high"}, 337 } 338 require.True(t, f.SetTask(task1)) 339 assert.False(t, f.Check(ni)) 340 341 // add label to Spec.Annotations.Labels 342 ni.Spec.Annotations.Labels["security"] = "low" 343 assert.False(t, f.Check(ni)) 344 ni.Spec.Annotations.Labels["security"] = "high" 345 assert.True(t, f.Check(ni)) 346 347 // extra label doesn't interfere 348 ni.Description.Engine.Labels["memory"] = "large" 349 assert.True(t, f.Check(ni)) 350 }