Add integration test for constraints
| ... | ... |
@@ -139,6 +139,26 @@ func (d *SwarmDaemon) getServiceTasks(c *check.C, service string) []swarm.Task {
|
| 139 | 139 |
return tasks |
| 140 | 140 |
} |
| 141 | 141 |
|
| 142 |
+func (d *SwarmDaemon) checkServiceRunningTasks(c *check.C, service string) func(*check.C) (interface{}, check.CommentInterface) {
|
|
| 143 |
+ return func(*check.C) (interface{}, check.CommentInterface) {
|
|
| 144 |
+ tasks := d.getServiceTasks(c, service) |
|
| 145 |
+ var runningCount int |
|
| 146 |
+ for _, task := range tasks {
|
|
| 147 |
+ if task.Status.State == swarm.TaskStateRunning {
|
|
| 148 |
+ runningCount++ |
|
| 149 |
+ } |
|
| 150 |
+ } |
|
| 151 |
+ return runningCount, nil |
|
| 152 |
+ } |
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+func (d *SwarmDaemon) checkServiceTasks(c *check.C, service string) func(*check.C) (interface{}, check.CommentInterface) {
|
|
| 156 |
+ return func(*check.C) (interface{}, check.CommentInterface) {
|
|
| 157 |
+ tasks := d.getServiceTasks(c, service) |
|
| 158 |
+ return len(tasks), nil |
|
| 159 |
+ } |
|
| 160 |
+} |
|
| 161 |
+ |
|
| 142 | 162 |
func (d *SwarmDaemon) checkRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) {
|
| 143 | 163 |
var tasks []swarm.Task |
| 144 | 164 |
|
| ... | ... |
@@ -312,6 +312,153 @@ func (s *DockerSwarmSuite) TestApiSwarmServicesUpdate(c *check.C) {
|
| 312 | 312 |
map[string]int{image2: instances})
|
| 313 | 313 |
} |
| 314 | 314 |
|
| 315 |
+func (s *DockerSwarmSuite) TestApiSwarmServiceConstraintRole(c *check.C) {
|
|
| 316 |
+ const nodeCount = 3 |
|
| 317 |
+ var daemons [nodeCount]*SwarmDaemon |
|
| 318 |
+ for i := 0; i < nodeCount; i++ {
|
|
| 319 |
+ daemons[i] = s.AddDaemon(c, true, i == 0) |
|
| 320 |
+ } |
|
| 321 |
+ // wait for nodes ready |
|
| 322 |
+ waitAndAssert(c, 5*time.Second, daemons[0].checkNodeReadyCount, checker.Equals, nodeCount) |
|
| 323 |
+ |
|
| 324 |
+ // create service |
|
| 325 |
+ constraints := []string{"node.role==worker"}
|
|
| 326 |
+ instances := 3 |
|
| 327 |
+ id := daemons[0].createService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) |
|
| 328 |
+ // wait for tasks ready |
|
| 329 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkServiceRunningTasks(c, id), checker.Equals, instances) |
|
| 330 |
+ // validate tasks are running on worker nodes |
|
| 331 |
+ tasks := daemons[0].getServiceTasks(c, id) |
|
| 332 |
+ for _, task := range tasks {
|
|
| 333 |
+ node := daemons[0].getNode(c, task.NodeID) |
|
| 334 |
+ c.Assert(node.Spec.Role, checker.Equals, swarm.NodeRoleWorker) |
|
| 335 |
+ } |
|
| 336 |
+ //remove service |
|
| 337 |
+ daemons[0].removeService(c, id) |
|
| 338 |
+ |
|
| 339 |
+ // create service |
|
| 340 |
+ constraints = []string{"node.role!=worker"}
|
|
| 341 |
+ id = daemons[0].createService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) |
|
| 342 |
+ // wait for tasks ready |
|
| 343 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkServiceRunningTasks(c, id), checker.Equals, instances) |
|
| 344 |
+ tasks = daemons[0].getServiceTasks(c, id) |
|
| 345 |
+ // validate tasks are running on manager nodes |
|
| 346 |
+ for _, task := range tasks {
|
|
| 347 |
+ node := daemons[0].getNode(c, task.NodeID) |
|
| 348 |
+ c.Assert(node.Spec.Role, checker.Equals, swarm.NodeRoleManager) |
|
| 349 |
+ } |
|
| 350 |
+ //remove service |
|
| 351 |
+ daemons[0].removeService(c, id) |
|
| 352 |
+ |
|
| 353 |
+ // create service |
|
| 354 |
+ constraints = []string{"node.role==nosuchrole"}
|
|
| 355 |
+ id = daemons[0].createService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) |
|
| 356 |
+ // wait for tasks created |
|
| 357 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkServiceTasks(c, id), checker.Equals, instances) |
|
| 358 |
+ // let scheduler try |
|
| 359 |
+ time.Sleep(250 * time.Millisecond) |
|
| 360 |
+ // validate tasks are not assigned to any node |
|
| 361 |
+ tasks = daemons[0].getServiceTasks(c, id) |
|
| 362 |
+ for _, task := range tasks {
|
|
| 363 |
+ c.Assert(task.NodeID, checker.Equals, "") |
|
| 364 |
+ } |
|
| 365 |
+} |
|
| 366 |
+ |
|
| 367 |
+func (s *DockerSwarmSuite) TestApiSwarmServiceConstraintLabel(c *check.C) {
|
|
| 368 |
+ const nodeCount = 3 |
|
| 369 |
+ var daemons [nodeCount]*SwarmDaemon |
|
| 370 |
+ for i := 0; i < nodeCount; i++ {
|
|
| 371 |
+ daemons[i] = s.AddDaemon(c, true, i == 0) |
|
| 372 |
+ } |
|
| 373 |
+ // wait for nodes ready |
|
| 374 |
+ waitAndAssert(c, 5*time.Second, daemons[0].checkNodeReadyCount, checker.Equals, nodeCount) |
|
| 375 |
+ nodes := daemons[0].listNodes(c) |
|
| 376 |
+ c.Assert(len(nodes), checker.Equals, nodeCount) |
|
| 377 |
+ |
|
| 378 |
+ // add labels to nodes |
|
| 379 |
+ daemons[0].updateNode(c, nodes[0].ID, func(n *swarm.Node) {
|
|
| 380 |
+ n.Spec.Annotations.Labels = map[string]string{
|
|
| 381 |
+ "security": "high", |
|
| 382 |
+ } |
|
| 383 |
+ }) |
|
| 384 |
+ for i := 1; i < nodeCount; i++ {
|
|
| 385 |
+ daemons[0].updateNode(c, nodes[i].ID, func(n *swarm.Node) {
|
|
| 386 |
+ n.Spec.Annotations.Labels = map[string]string{
|
|
| 387 |
+ "security": "low", |
|
| 388 |
+ } |
|
| 389 |
+ }) |
|
| 390 |
+ } |
|
| 391 |
+ |
|
| 392 |
+ // create service |
|
| 393 |
+ instances := 3 |
|
| 394 |
+ constraints := []string{"node.labels.security==high"}
|
|
| 395 |
+ id := daemons[0].createService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) |
|
| 396 |
+ // wait for tasks ready |
|
| 397 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkServiceRunningTasks(c, id), checker.Equals, instances) |
|
| 398 |
+ tasks := daemons[0].getServiceTasks(c, id) |
|
| 399 |
+ // validate all tasks are running on nodes[0] |
|
| 400 |
+ for _, task := range tasks {
|
|
| 401 |
+ c.Assert(task.NodeID, checker.Equals, nodes[0].ID) |
|
| 402 |
+ } |
|
| 403 |
+ //remove service |
|
| 404 |
+ daemons[0].removeService(c, id) |
|
| 405 |
+ |
|
| 406 |
+ // create service |
|
| 407 |
+ constraints = []string{"node.labels.security!=high"}
|
|
| 408 |
+ id = daemons[0].createService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) |
|
| 409 |
+ // wait for tasks ready |
|
| 410 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkServiceRunningTasks(c, id), checker.Equals, instances) |
|
| 411 |
+ tasks = daemons[0].getServiceTasks(c, id) |
|
| 412 |
+ // validate all tasks are NOT running on nodes[0] |
|
| 413 |
+ for _, task := range tasks {
|
|
| 414 |
+ c.Assert(task.NodeID, checker.Not(checker.Equals), nodes[0].ID) |
|
| 415 |
+ } |
|
| 416 |
+ //remove service |
|
| 417 |
+ daemons[0].removeService(c, id) |
|
| 418 |
+ |
|
| 419 |
+ constraints = []string{"node.labels.security==medium"}
|
|
| 420 |
+ id = daemons[0].createService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) |
|
| 421 |
+ // wait for tasks created |
|
| 422 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkServiceTasks(c, id), checker.Equals, instances) |
|
| 423 |
+ // let scheduler try |
|
| 424 |
+ time.Sleep(250 * time.Millisecond) |
|
| 425 |
+ tasks = daemons[0].getServiceTasks(c, id) |
|
| 426 |
+ // validate tasks are not assigned |
|
| 427 |
+ for _, task := range tasks {
|
|
| 428 |
+ c.Assert(task.NodeID, checker.Equals, "") |
|
| 429 |
+ } |
|
| 430 |
+ //remove service |
|
| 431 |
+ daemons[0].removeService(c, id) |
|
| 432 |
+ |
|
| 433 |
+ // multiple constraints |
|
| 434 |
+ constraints = []string{
|
|
| 435 |
+ "node.labels.security==high", |
|
| 436 |
+ fmt.Sprintf("node.id==%s", nodes[1].ID),
|
|
| 437 |
+ } |
|
| 438 |
+ id = daemons[0].createService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) |
|
| 439 |
+ // wait for tasks created |
|
| 440 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkServiceTasks(c, id), checker.Equals, instances) |
|
| 441 |
+ // let scheduler try |
|
| 442 |
+ time.Sleep(250 * time.Millisecond) |
|
| 443 |
+ tasks = daemons[0].getServiceTasks(c, id) |
|
| 444 |
+ // validate tasks are not assigned |
|
| 445 |
+ for _, task := range tasks {
|
|
| 446 |
+ c.Assert(task.NodeID, checker.Equals, "") |
|
| 447 |
+ } |
|
| 448 |
+ // make nodes[1] fulfills the constraints |
|
| 449 |
+ daemons[0].updateNode(c, nodes[1].ID, func(n *swarm.Node) {
|
|
| 450 |
+ n.Spec.Annotations.Labels = map[string]string{
|
|
| 451 |
+ "security": "high", |
|
| 452 |
+ } |
|
| 453 |
+ }) |
|
| 454 |
+ // wait for tasks ready |
|
| 455 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkServiceRunningTasks(c, id), checker.Equals, instances) |
|
| 456 |
+ tasks = daemons[0].getServiceTasks(c, id) |
|
| 457 |
+ for _, task := range tasks {
|
|
| 458 |
+ c.Assert(task.NodeID, checker.Equals, nodes[1].ID) |
|
| 459 |
+ } |
|
| 460 |
+} |
|
| 461 |
+ |
|
| 315 | 462 |
func (s *DockerSwarmSuite) TestApiSwarmServicesStateReporting(c *check.C) {
|
| 316 | 463 |
testRequires(c, SameHostDaemon) |
| 317 | 464 |
testRequires(c, DaemonIsLinux) |
| ... | ... |
@@ -834,6 +981,15 @@ func setImage(image string) serviceConstructor {
|
| 834 | 834 |
} |
| 835 | 835 |
} |
| 836 | 836 |
|
| 837 |
+func setConstraints(constraints []string) serviceConstructor {
|
|
| 838 |
+ return func(s *swarm.Service) {
|
|
| 839 |
+ if s.Spec.TaskTemplate.Placement == nil {
|
|
| 840 |
+ s.Spec.TaskTemplate.Placement = &swarm.Placement{}
|
|
| 841 |
+ } |
|
| 842 |
+ s.Spec.TaskTemplate.Placement.Constraints = constraints |
|
| 843 |
+ } |
|
| 844 |
+} |
|
| 845 |
+ |
|
| 837 | 846 |
func setGlobalMode(s *swarm.Service) {
|
| 838 | 847 |
s.Spec.Mode = swarm.ServiceMode{
|
| 839 | 848 |
Global: &swarm.GlobalService{},
|