github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/monitor/host_flagging_test.go (about) 1 package monitor 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/evergreen-ci/evergreen" 8 "github.com/evergreen-ci/evergreen/cloud/providers/mock" 9 "github.com/evergreen-ci/evergreen/db" 10 "github.com/evergreen-ci/evergreen/model/distro" 11 "github.com/evergreen-ci/evergreen/model/host" 12 modelUtil "github.com/evergreen-ci/evergreen/model/testutil" 13 "github.com/evergreen-ci/evergreen/testutil" 14 "github.com/evergreen-ci/evergreen/util" 15 . "github.com/smartystreets/goconvey/convey" 16 ) 17 18 func TestFlaggingDecommissionedHosts(t *testing.T) { 19 20 testConfig := testutil.TestConfig() 21 22 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testConfig)) 23 24 Convey("When flagging decommissioned hosts", t, func() { 25 26 Convey("only hosts in the database who are marked decommissioned"+ 27 " should be returned", func() { 28 29 // reset the db 30 testutil.HandleTestingErr(db.ClearCollections(host.Collection), 31 t, "error clearing hosts collection") 32 33 // insert hosts with different statuses 34 35 host1 := &host.Host{ 36 Id: "h1", 37 Status: evergreen.HostRunning, 38 } 39 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 40 41 host2 := &host.Host{ 42 Id: "h2", 43 Status: evergreen.HostTerminated, 44 } 45 testutil.HandleTestingErr(host2.Insert(), t, "error inserting host") 46 47 host3 := &host.Host{ 48 Id: "h3", 49 Status: evergreen.HostDecommissioned, 50 } 51 testutil.HandleTestingErr(host3.Insert(), t, "error inserting host") 52 53 host4 := &host.Host{ 54 Id: "h4", 55 Status: evergreen.HostDecommissioned, 56 } 57 testutil.HandleTestingErr(host4.Insert(), t, "error inserting host") 58 59 host5 := &host.Host{ 60 Id: "h5", 61 Status: evergreen.HostQuarantined, 62 } 63 testutil.HandleTestingErr(host5.Insert(), t, "error inserting host") 64 65 // flag the decommissioned hosts - there should be 2 of them 66 decommissioned, err := flagDecommissionedHosts(nil, testConfig) 67 So(err, ShouldBeNil) 68 So(len(decommissioned), ShouldEqual, 2) 69 var ids []string 70 for _, h := range decommissioned { 71 ids = append(ids, h.Id) 72 } 73 So(util.SliceContains(ids, host3.Id), ShouldBeTrue) 74 So(util.SliceContains(ids, host4.Id), ShouldBeTrue) 75 }) 76 77 }) 78 79 } 80 81 func TestFlaggingIdleHosts(t *testing.T) { 82 83 testConfig := testutil.TestConfig() 84 85 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testConfig)) 86 87 Convey("When flagging idle hosts to be terminated", t, func() { 88 89 // reset the db 90 testutil.HandleTestingErr(db.ClearCollections(host.Collection), 91 t, "error clearing hosts collection") 92 testutil.HandleTestingErr(modelUtil.AddTestIndexes(host.Collection, 93 true, true, host.RunningTaskKey), t, "error adding host index") 94 Convey("hosts currently running a task should never be"+ 95 " flagged", func() { 96 97 // insert a host that is currently running a task - but whose 98 // creation time would otherwise indicate it has been idle a while 99 host1 := host.Host{ 100 Id: "h1", 101 Provider: mock.ProviderName, 102 CreationTime: time.Now().Add(-30 * time.Minute), 103 RunningTask: "t1", 104 Status: evergreen.HostRunning, 105 StartedBy: evergreen.User, 106 } 107 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 108 109 // finding idle hosts should not return the host 110 idle, err := flagIdleHosts(nil, nil) 111 So(err, ShouldBeNil) 112 So(len(idle), ShouldEqual, 0) 113 114 Convey("even if they have a last communication time > 10 minutes", func() { 115 h2 := host.Host{ 116 Id: "anotherhost", 117 Provider: mock.ProviderName, 118 CreationTime: time.Now().Add(-30 * time.Minute), 119 RunningTask: "t3", 120 Status: evergreen.HostRunning, 121 LastCommunicationTime: time.Now().Add(-30 * time.Minute), 122 StartedBy: evergreen.User, 123 } 124 testutil.HandleTestingErr(h2.Insert(), t, "error inserting host") 125 // finding idle hosts should not return the host 126 idle, err := flagIdleHosts(nil, nil) 127 So(err, ShouldBeNil) 128 So(len(idle), ShouldEqual, 0) 129 130 }) 131 132 }) 133 134 Convey("hosts not currently running a task should be flagged if they"+ 135 " have been idle at least 15 minutes and will incur a payment in"+ 136 " less than 10 minutes", func() { 137 138 // insert two hosts - one whose last task was more than 15 minutes 139 // ago, one whose last task was less than 15 minutes ago 140 141 host1 := host.Host{ 142 Id: "h2", 143 Provider: mock.ProviderName, 144 LastTaskCompleted: "t1", 145 LastTaskCompletedTime: time.Now().Add(-time.Minute * 20), 146 LastCommunicationTime: time.Now(), 147 Status: evergreen.HostRunning, 148 StartedBy: evergreen.User, 149 } 150 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 151 152 host2 := host.Host{ 153 Id: "h3", 154 Provider: mock.ProviderName, 155 LastTaskCompleted: "t2", 156 LastTaskCompletedTime: time.Now().Add(-time.Minute * 5), 157 LastCommunicationTime: time.Now(), 158 Status: evergreen.HostRunning, 159 StartedBy: evergreen.User, 160 } 161 testutil.HandleTestingErr(host2.Insert(), t, "error inserting host") 162 163 // finding idle hosts should only return the first host 164 idle, err := flagIdleHosts(nil, nil) 165 So(err, ShouldBeNil) 166 So(len(idle), ShouldEqual, 1) 167 So(idle[0].Id, ShouldEqual, "h2") 168 169 }) 170 Convey("hosts not currently running a task with a last communication time greater"+ 171 "than 10 mins should be marked as idle", func() { 172 anotherHost := host.Host{ 173 Id: "h1", 174 Provider: mock.ProviderName, 175 LastCommunicationTime: time.Now().Add(-time.Minute * 20), 176 Status: evergreen.HostRunning, 177 StartedBy: evergreen.User, 178 } 179 So(anotherHost.Insert(), ShouldBeNil) 180 // finding idle hosts should only return the first host 181 idle, err := flagIdleHosts(nil, nil) 182 So(err, ShouldBeNil) 183 So(len(idle), ShouldEqual, 1) 184 So(idle[0].Id, ShouldEqual, "h1") 185 }) 186 187 }) 188 189 } 190 191 func TestFlaggingExcessHosts(t *testing.T) { 192 193 testConfig := testutil.TestConfig() 194 195 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testConfig)) 196 197 Convey("When flagging excess hosts to be terminated", t, func() { 198 199 Convey("with two separate distros containing hosts", func() { 200 201 // reset the db 202 testutil.HandleTestingErr(db.ClearCollections(host.Collection), 203 t, "error clearing hosts collection") 204 205 // mock up the distros 206 207 distro1 := distro.Distro{ 208 Id: "d1", 209 PoolSize: 2, 210 } 211 distro2 := distro.Distro{ 212 Id: "d2", 213 PoolSize: 1, 214 } 215 distros := []distro.Distro{distro1, distro2} 216 217 Convey("if neither distro has excess hosts, no hosts should be"+ 218 " flagged to be terminated", func() { 219 220 // insert one host for each distro 221 222 host1 := &host.Host{ 223 Id: "h1", 224 Distro: distro.Distro{Id: "d1"}, 225 Status: evergreen.HostRunning, 226 StartedBy: evergreen.User, 227 Provider: mock.ProviderName, 228 } 229 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 230 231 host2 := &host.Host{ 232 Id: "h2", 233 Distro: distro.Distro{Id: "d2"}, 234 Status: evergreen.HostRunning, 235 StartedBy: evergreen.User, 236 Provider: mock.ProviderName, 237 } 238 testutil.HandleTestingErr(host2.Insert(), t, "error inserting host") 239 240 // flag the excess hosts - there should not be any 241 excess, err := flagExcessHosts(distros, nil) 242 So(err, ShouldBeNil) 243 So(len(excess), ShouldEqual, 0) 244 245 }) 246 247 Convey("if only one distro has excess hosts, the appropriate"+ 248 " number of hosts from that distro should be flagged", func() { 249 250 // insert one host for the first distro, and three for the 251 // second distro 252 253 host1 := &host.Host{ 254 Id: "h1", 255 Distro: distro.Distro{Id: "d1"}, 256 Status: evergreen.HostRunning, 257 StartedBy: evergreen.User, 258 Provider: mock.ProviderName, 259 } 260 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 261 262 host2 := &host.Host{ 263 Id: "h2", 264 Distro: distro.Distro{Id: "d2"}, 265 Status: evergreen.HostRunning, 266 StartedBy: evergreen.User, 267 Provider: mock.ProviderName, 268 } 269 testutil.HandleTestingErr(host2.Insert(), t, "error inserting host") 270 271 host3 := &host.Host{ 272 Id: "h3", 273 Distro: distro.Distro{Id: "d2"}, 274 Status: evergreen.HostRunning, 275 StartedBy: evergreen.User, 276 Provider: mock.ProviderName, 277 } 278 testutil.HandleTestingErr(host3.Insert(), t, "error inserting host") 279 280 host4 := &host.Host{ 281 Id: "h4", 282 Distro: distro.Distro{Id: "d2"}, 283 Status: evergreen.HostRunning, 284 StartedBy: evergreen.User, 285 Provider: mock.ProviderName, 286 } 287 testutil.HandleTestingErr(host4.Insert(), t, "error inserting host") 288 289 // flag the excess hosts - there should be 2, both from 290 // the second distro 291 excess, err := flagExcessHosts(distros, nil) 292 So(err, ShouldBeNil) 293 So(len(excess), ShouldEqual, 2) 294 for _, host := range excess { 295 So(host.Distro.Id, ShouldEqual, "d2") 296 } 297 298 }) 299 300 Convey("if both distros have excess hosts, the appropriate number"+ 301 " of hosts from each distro should be flagged", func() { 302 303 // insert three hosts for each distro 304 305 host1 := &host.Host{ 306 Id: "h1", 307 Distro: distro.Distro{Id: "d1"}, 308 Status: evergreen.HostRunning, 309 StartedBy: evergreen.User, 310 Provider: mock.ProviderName, 311 } 312 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 313 314 host2 := &host.Host{ 315 Id: "h2", 316 Distro: distro.Distro{Id: "d1"}, 317 Status: evergreen.HostRunning, 318 StartedBy: evergreen.User, 319 Provider: mock.ProviderName, 320 } 321 testutil.HandleTestingErr(host2.Insert(), t, "error inserting host") 322 323 host3 := &host.Host{ 324 Id: "h3", 325 Distro: distro.Distro{Id: "d1"}, 326 Status: evergreen.HostRunning, 327 StartedBy: evergreen.User, 328 Provider: mock.ProviderName, 329 } 330 testutil.HandleTestingErr(host3.Insert(), t, "error inserting host") 331 332 host4 := &host.Host{ 333 Id: "h4", 334 Distro: distro.Distro{Id: "d2"}, 335 Status: evergreen.HostRunning, 336 StartedBy: evergreen.User, 337 Provider: mock.ProviderName, 338 } 339 testutil.HandleTestingErr(host4.Insert(), t, "error inserting host") 340 341 host5 := &host.Host{ 342 Id: "h5", 343 Distro: distro.Distro{Id: "d2"}, 344 Status: evergreen.HostRunning, 345 StartedBy: evergreen.User, 346 Provider: mock.ProviderName, 347 } 348 testutil.HandleTestingErr(host5.Insert(), t, "error inserting host") 349 350 host6 := &host.Host{ 351 Id: "h6", 352 Distro: distro.Distro{Id: "d2"}, 353 Status: evergreen.HostRunning, 354 StartedBy: evergreen.User, 355 Provider: mock.ProviderName, 356 } 357 testutil.HandleTestingErr(host6.Insert(), t, "error inserting host") 358 359 // find the excess hosts - there should be one for the first 360 // distro and two for the second 361 excess, err := flagExcessHosts(distros, nil) 362 So(err, ShouldBeNil) 363 So(len(excess), ShouldEqual, 3) 364 excessFirst := 0 365 excessSecond := 0 366 for _, host := range excess { 367 if host.Distro.Id == "d1" { 368 excessFirst++ 369 } 370 if host.Distro.Id == "d2" { 371 excessSecond++ 372 } 373 } 374 So(excessFirst, ShouldEqual, 1) 375 So(excessSecond, ShouldEqual, 2) 376 377 }) 378 379 Convey("hosts currently running a task should not be"+ 380 " flagged", func() { 381 382 // insert two hosts for each distro, with running tasks 383 384 host1 := &host.Host{ 385 Id: "h1", 386 Distro: distro.Distro{Id: "d1"}, 387 Status: evergreen.HostRunning, 388 StartedBy: evergreen.User, 389 RunningTask: "t1", 390 Provider: mock.ProviderName, 391 } 392 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 393 394 host2 := &host.Host{ 395 Id: "h2", 396 Distro: distro.Distro{Id: "d1"}, 397 Status: evergreen.HostRunning, 398 StartedBy: evergreen.User, 399 RunningTask: "t2", 400 Provider: mock.ProviderName, 401 } 402 testutil.HandleTestingErr(host2.Insert(), t, "error inserting host") 403 404 host3 := &host.Host{ 405 Id: "h3", 406 Distro: distro.Distro{Id: "d2"}, 407 Status: evergreen.HostRunning, 408 StartedBy: evergreen.User, 409 RunningTask: "t3", 410 Provider: mock.ProviderName, 411 } 412 testutil.HandleTestingErr(host3.Insert(), t, "error inserting host") 413 414 host4 := &host.Host{ 415 Id: "h4", 416 Distro: distro.Distro{Id: "d2"}, 417 Status: evergreen.HostRunning, 418 StartedBy: evergreen.User, 419 RunningTask: "t4", 420 Provider: mock.ProviderName, 421 } 422 testutil.HandleTestingErr(host4.Insert(), t, "error inserting host") 423 424 // find the excess hosts - there should be none, since all of 425 // the hosts are running tasks and cannot safely be terminated 426 excess, err := flagExcessHosts(distros, nil) 427 So(err, ShouldBeNil) 428 So(len(excess), ShouldEqual, 0) 429 430 }) 431 432 }) 433 434 }) 435 436 } 437 438 func TestFlaggingUnprovisionedHosts(t *testing.T) { 439 440 testConfig := testutil.TestConfig() 441 442 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testConfig)) 443 444 Convey("When flagging unprovisioned hosts to be terminated", t, func() { 445 446 // reset the db 447 testutil.HandleTestingErr(db.ClearCollections(host.Collection), 448 t, "error clearing hosts collection") 449 450 Convey("hosts that have not hit the provisioning limit should"+ 451 " be ignored", func() { 452 453 host1 := &host.Host{ 454 Id: "h1", 455 StartedBy: evergreen.User, 456 CreationTime: time.Now().Add(-time.Minute * 10), 457 } 458 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 459 460 unprovisioned, err := flagUnprovisionedHosts(nil, nil) 461 So(err, ShouldBeNil) 462 So(len(unprovisioned), ShouldEqual, 0) 463 464 }) 465 466 Convey("hosts that are already terminated should be ignored", func() { 467 468 host1 := &host.Host{ 469 Id: "h1", 470 StartedBy: evergreen.User, 471 CreationTime: time.Now().Add(-time.Minute * 40), 472 Status: evergreen.HostTerminated, 473 } 474 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 475 476 unprovisioned, err := flagUnprovisionedHosts(nil, nil) 477 So(err, ShouldBeNil) 478 So(len(unprovisioned), ShouldEqual, 0) 479 480 }) 481 482 Convey("hosts that are already provisioned should be ignored", func() { 483 484 host1 := &host.Host{ 485 Id: "h1", 486 StartedBy: evergreen.User, 487 CreationTime: time.Now().Add(-time.Minute * 40), 488 Provisioned: true, 489 } 490 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 491 492 unprovisioned, err := flagUnprovisionedHosts(nil, nil) 493 So(err, ShouldBeNil) 494 So(len(unprovisioned), ShouldEqual, 0) 495 496 }) 497 498 Convey("hosts that have hit the provisioning limit should be"+ 499 " flagged", func() { 500 501 host1 := &host.Host{ 502 Id: "h1", 503 StartedBy: evergreen.User, 504 CreationTime: time.Now().Add(-time.Minute * 40), 505 } 506 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 507 508 unprovisioned, err := flagUnprovisionedHosts(nil, nil) 509 So(err, ShouldBeNil) 510 So(len(unprovisioned), ShouldEqual, 1) 511 So(unprovisioned[0].Id, ShouldEqual, "h1") 512 513 }) 514 515 }) 516 } 517 518 func TestFlaggingProvisioningFailedHosts(t *testing.T) { 519 520 testConfig := testutil.TestConfig() 521 522 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testConfig)) 523 524 Convey("When flagging hosts whose provisioning failed", t, func() { 525 526 // reset the db 527 testutil.HandleTestingErr(db.ClearCollections(host.Collection), 528 t, "error clearing hosts collection") 529 530 Convey("only hosts whose provisioning failed should be"+ 531 " picked up", func() { 532 533 host1 := &host.Host{ 534 Id: "h1", 535 Status: evergreen.HostRunning, 536 } 537 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 538 539 host2 := &host.Host{ 540 Id: "h2", 541 Status: evergreen.HostUninitialized, 542 } 543 testutil.HandleTestingErr(host2.Insert(), t, "error inserting host") 544 545 host3 := &host.Host{ 546 Id: "h3", 547 Status: evergreen.HostProvisionFailed, 548 } 549 testutil.HandleTestingErr(host3.Insert(), t, "error inserting host") 550 551 unprovisioned, err := flagProvisioningFailedHosts(nil, nil) 552 So(err, ShouldBeNil) 553 So(len(unprovisioned), ShouldEqual, 1) 554 So(unprovisioned[0].Id, ShouldEqual, "h3") 555 556 }) 557 558 }) 559 } 560 561 func TestFlaggingExpiredHosts(t *testing.T) { 562 563 testConfig := testutil.TestConfig() 564 565 db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testConfig)) 566 567 Convey("When flagging expired hosts to be terminated", t, func() { 568 569 // reset the db 570 testutil.HandleTestingErr(db.ClearCollections(host.Collection), 571 t, "error clearing hosts collection") 572 573 Convey("hosts started by the default user should be filtered"+ 574 " out", func() { 575 576 host1 := &host.Host{ 577 Id: "h1", 578 Status: evergreen.HostRunning, 579 StartedBy: evergreen.User, 580 } 581 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 582 583 expired, err := flagExpiredHosts(nil, nil) 584 So(err, ShouldBeNil) 585 So(len(expired), ShouldEqual, 0) 586 587 }) 588 589 Convey("hosts that are terminated or quarantined should be filtered"+ 590 " out", func() { 591 592 host1 := &host.Host{ 593 Id: "h1", 594 Status: evergreen.HostQuarantined, 595 } 596 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 597 598 host2 := &host.Host{ 599 Id: "h2", 600 Status: evergreen.HostTerminated, 601 } 602 testutil.HandleTestingErr(host2.Insert(), t, "error inserting host") 603 604 expired, err := flagExpiredHosts(nil, nil) 605 So(err, ShouldBeNil) 606 So(len(expired), ShouldEqual, 0) 607 608 }) 609 610 Convey("hosts should be returned if their expiration threshold has"+ 611 " been reached", func() { 612 613 // not expired 614 host1 := &host.Host{ 615 Id: "h1", 616 Status: evergreen.HostRunning, 617 ExpirationTime: time.Now().Add(time.Minute * 10), 618 } 619 testutil.HandleTestingErr(host1.Insert(), t, "error inserting host") 620 621 // expired 622 host2 := &host.Host{ 623 Id: "h2", 624 Status: evergreen.HostRunning, 625 ExpirationTime: time.Now().Add(-time.Minute * 10), 626 } 627 testutil.HandleTestingErr(host2.Insert(), t, "error inserting host") 628 629 expired, err := flagExpiredHosts(nil, nil) 630 So(err, ShouldBeNil) 631 So(len(expired), ShouldEqual, 1) 632 So(expired[0].Id, ShouldEqual, "h2") 633 634 }) 635 636 }) 637 638 }