github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/model/host/host_test.go (about)

     1  package host
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/evergreen-ci/evergreen"
     9  	"github.com/evergreen-ci/evergreen/db"
    10  	"github.com/evergreen-ci/evergreen/model/distro"
    11  	"github.com/evergreen-ci/evergreen/testutil"
    12  	. "github.com/smartystreets/goconvey/convey"
    13  	"gopkg.in/mgo.v2/bson"
    14  )
    15  
    16  func init() {
    17  	db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testutil.TestConfig()))
    18  }
    19  
    20  func hostIdInSlice(hosts []Host, id string) bool {
    21  	for _, host := range hosts {
    22  		if host.Id == id {
    23  			return true
    24  		}
    25  	}
    26  	return false
    27  }
    28  
    29  func TestGenericHostFinding(t *testing.T) {
    30  
    31  	Convey("When finding hosts", t, func() {
    32  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error clearing"+
    33  			" '%v' collection", Collection)
    34  
    35  		Convey("when finding one host", func() {
    36  			Convey("the matching host should be returned", func() {
    37  				matchingHost := &Host{
    38  					Id: "matches",
    39  				}
    40  				So(matchingHost.Insert(), ShouldBeNil)
    41  
    42  				nonMatchingHost := &Host{
    43  					Id: "nonMatches",
    44  				}
    45  				So(nonMatchingHost.Insert(), ShouldBeNil)
    46  
    47  				found, err := FindOne(ById(matchingHost.Id))
    48  				So(err, ShouldBeNil)
    49  				So(found.Id, ShouldEqual, matchingHost.Id)
    50  
    51  			})
    52  		})
    53  
    54  		Convey("when finding multiple hosts", func() {
    55  			dId := fmt.Sprintf("%v.%v", DistroKey, distro.IdKey)
    56  
    57  			Convey("the hosts matching the query should be returned", func() {
    58  				matchingHostOne := &Host{
    59  					Id:     "matches",
    60  					Distro: distro.Distro{Id: "d1"},
    61  				}
    62  				So(matchingHostOne.Insert(), ShouldBeNil)
    63  
    64  				matchingHostTwo := &Host{
    65  					Id:     "matchesAlso",
    66  					Distro: distro.Distro{Id: "d1"},
    67  				}
    68  				So(matchingHostTwo.Insert(), ShouldBeNil)
    69  
    70  				nonMatchingHost := &Host{
    71  					Id:     "nonMatches",
    72  					Distro: distro.Distro{Id: "d2"},
    73  				}
    74  				So(nonMatchingHost.Insert(), ShouldBeNil)
    75  
    76  				found, err := Find(db.Query(bson.M{dId: "d1"}))
    77  				So(err, ShouldBeNil)
    78  				So(len(found), ShouldEqual, 2)
    79  				So(hostIdInSlice(found, matchingHostOne.Id), ShouldBeTrue)
    80  				So(hostIdInSlice(found, matchingHostTwo.Id), ShouldBeTrue)
    81  
    82  			})
    83  
    84  			Convey("when querying two hosts for running tasks", func() {
    85  				matchingHost := &Host{Id: "task", Status: evergreen.HostRunning, RunningTask: "t1"}
    86  				So(matchingHost.Insert(), ShouldBeNil)
    87  				nonMatchingHost := &Host{Id: "nope", Status: evergreen.HostRunning}
    88  				So(nonMatchingHost.Insert(), ShouldBeNil)
    89  				Convey("the host with the running task should be returned", func() {
    90  					found, err := Find(IsRunningTask)
    91  					So(err, ShouldBeNil)
    92  					So(len(found), ShouldEqual, 1)
    93  					So(found[0].Id, ShouldEqual, matchingHost.Id)
    94  				})
    95  			})
    96  
    97  			Convey("the specified projection, sort, skip, and limit should be used", func() {
    98  				matchingHostOne := &Host{
    99  					Id:     "matches",
   100  					Host:   "hostOne",
   101  					Distro: distro.Distro{Id: "d1"},
   102  					Tag:    "2",
   103  				}
   104  				So(matchingHostOne.Insert(), ShouldBeNil)
   105  
   106  				matchingHostTwo := &Host{
   107  					Id:     "matchesAlso",
   108  					Host:   "hostTwo",
   109  					Distro: distro.Distro{Id: "d1"},
   110  					Tag:    "1",
   111  				}
   112  				So(matchingHostTwo.Insert(), ShouldBeNil)
   113  
   114  				matchingHostThree := &Host{
   115  					Id:     "stillMatches",
   116  					Host:   "hostThree",
   117  					Distro: distro.Distro{Id: "d1"},
   118  					Tag:    "3",
   119  				}
   120  				So(matchingHostThree.Insert(), ShouldBeNil)
   121  
   122  				// find the hosts, removing the host field from the projection,
   123  				// sorting by tag, skipping one, and limiting to one
   124  
   125  				found, err := Find(db.Query(bson.M{dId: "d1"}).
   126  					WithoutFields(DNSKey).
   127  					Sort([]string{TagKey}).
   128  					Skip(1).Limit(1))
   129  				So(err, ShouldBeNil)
   130  				So(len(found), ShouldEqual, 1)
   131  				So(found[0].Id, ShouldEqual, matchingHostOne.Id)
   132  				So(found[0].Host, ShouldEqual, "") // filtered out in projection
   133  			})
   134  		})
   135  	})
   136  }
   137  
   138  func TestFindingHostsWithRunningTasks(t *testing.T) {
   139  	Convey("With a host with no running task that is not terminated", t, func() {
   140  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error clearing"+
   141  			" '%v' collection", Collection)
   142  		h := Host{
   143  			Id:     "sample_host",
   144  			Status: evergreen.HostRunning,
   145  		}
   146  		So(h.Insert(), ShouldBeNil)
   147  		found, err := Find(IsRunningTask)
   148  		So(err, ShouldBeNil)
   149  		So(len(found), ShouldEqual, 0)
   150  		Convey("with a host that is terminated with no running task", func() {
   151  			testutil.HandleTestingErr(db.Clear(Collection), t, "Error clearing"+
   152  				" '%v' collection", Collection)
   153  			h1 := Host{
   154  				Id:     "another",
   155  				Status: evergreen.HostTerminated,
   156  			}
   157  			So(h1.Insert(), ShouldBeNil)
   158  			found, err = Find(IsRunningTask)
   159  			So(err, ShouldBeNil)
   160  			So(len(found), ShouldEqual, 0)
   161  		})
   162  	})
   163  
   164  }
   165  
   166  func TestMonitorHosts(t *testing.T) {
   167  	Convey("With a host with no reachability check", t, func() {
   168  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error clearing"+
   169  			" '%v' collection", Collection)
   170  		now := time.Now()
   171  		h := Host{
   172  			Id:        "sample_host",
   173  			Status:    evergreen.HostRunning,
   174  			StartedBy: evergreen.User,
   175  		}
   176  		So(h.Insert(), ShouldBeNil)
   177  		found, err := Find(ByNotMonitoredSince(now))
   178  		So(err, ShouldBeNil)
   179  		So(len(found), ShouldEqual, 1)
   180  		Convey("a host that has a running task and no reachability check should not return", func() {
   181  			testutil.HandleTestingErr(db.Clear(Collection), t, "Error clearing"+
   182  				" '%v' collection", Collection)
   183  			anotherHost := Host{
   184  				Id:          "anotherHost",
   185  				Status:      evergreen.HostRunning,
   186  				StartedBy:   evergreen.User,
   187  				RunningTask: "id",
   188  			}
   189  			So(anotherHost.Insert(), ShouldBeNil)
   190  			found, err := Find(ByNotMonitoredSince(now))
   191  			So(err, ShouldBeNil)
   192  			So(len(found), ShouldEqual, 0)
   193  		})
   194  	})
   195  }
   196  
   197  func TestUpdatingHostStatus(t *testing.T) {
   198  
   199  	Convey("With a host", t, func() {
   200  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   201  			" clearing '%v' collection", Collection)
   202  
   203  		var err error
   204  
   205  		host := &Host{
   206  			Id: "hostOne",
   207  		}
   208  
   209  		So(host.Insert(), ShouldBeNil)
   210  
   211  		Convey("setting the host's status should update both the in-memory"+
   212  			" and database versions of the host", func() {
   213  
   214  			So(host.SetStatus(evergreen.HostRunning), ShouldBeNil)
   215  			So(host.Status, ShouldEqual, evergreen.HostRunning)
   216  
   217  			host, err = FindOne(ById(host.Id))
   218  			So(err, ShouldBeNil)
   219  			So(host.Status, ShouldEqual, evergreen.HostRunning)
   220  
   221  		})
   222  
   223  		Convey("if the host is terminated, the status update should fail"+
   224  			" with an error", func() {
   225  
   226  			So(host.SetStatus(evergreen.HostTerminated), ShouldBeNil)
   227  			So(host.SetStatus(evergreen.HostRunning), ShouldNotBeNil)
   228  			So(host.Status, ShouldEqual, evergreen.HostTerminated)
   229  
   230  			host, err = FindOne(ById(host.Id))
   231  			So(err, ShouldBeNil)
   232  			So(host.Status, ShouldEqual, evergreen.HostTerminated)
   233  
   234  		})
   235  
   236  	})
   237  
   238  }
   239  
   240  func TestSetHostTerminated(t *testing.T) {
   241  
   242  	Convey("With a host", t, func() {
   243  
   244  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   245  			" clearing '%v' collection", Collection)
   246  
   247  		var err error
   248  
   249  		host := &Host{
   250  			Id: "hostOne",
   251  		}
   252  
   253  		So(host.Insert(), ShouldBeNil)
   254  
   255  		Convey("setting the host as terminated should set the status and the"+
   256  			" termination time in both the in-memory and database copies of"+
   257  			" the host", func() {
   258  
   259  			So(host.Terminate(), ShouldBeNil)
   260  			So(host.Status, ShouldEqual, evergreen.HostTerminated)
   261  			So(host.TerminationTime.IsZero(), ShouldBeFalse)
   262  
   263  			host, err = FindOne(ById(host.Id))
   264  			So(err, ShouldBeNil)
   265  			So(host.Status, ShouldEqual, evergreen.HostTerminated)
   266  			So(host.TerminationTime.IsZero(), ShouldBeFalse)
   267  
   268  		})
   269  
   270  	})
   271  }
   272  
   273  func TestHostSetDNSName(t *testing.T) {
   274  	var err error
   275  
   276  	Convey("With a host", t, func() {
   277  
   278  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   279  			" clearing '%v' collection", Collection)
   280  
   281  		host := &Host{
   282  			Id: "hostOne",
   283  		}
   284  
   285  		So(host.Insert(), ShouldBeNil)
   286  
   287  		Convey("setting the hostname should update both the in-memory and"+
   288  			" database copies of the host", func() {
   289  
   290  			So(host.SetDNSName("hostname"), ShouldBeNil)
   291  			So(host.Host, ShouldEqual, "hostname")
   292  			host, err = FindOne(ById(host.Id))
   293  			So(err, ShouldBeNil)
   294  			So(host.Host, ShouldEqual, "hostname")
   295  
   296  			// if the host is already updated, no new updates should work
   297  			So(host.SetDNSName("hostname2"), ShouldBeNil)
   298  			So(host.Host, ShouldEqual, "hostname")
   299  
   300  			host, err = FindOne(ById(host.Id))
   301  			So(err, ShouldBeNil)
   302  			So(host.Host, ShouldEqual, "hostname")
   303  
   304  		})
   305  
   306  	})
   307  }
   308  
   309  func TestMarkAsProvisioned(t *testing.T) {
   310  
   311  	Convey("With a host", t, func() {
   312  
   313  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   314  			" clearing '%v' collection", Collection)
   315  
   316  		var err error
   317  
   318  		host := &Host{
   319  			Id: "hostOne",
   320  		}
   321  
   322  		So(host.Insert(), ShouldBeNil)
   323  
   324  		Convey("marking the host as provisioned should update the status,"+
   325  			" provisioned, and host name fields in both the in-memory and"+
   326  			" database copies of the host", func() {
   327  
   328  			So(host.MarkAsProvisioned(), ShouldBeNil)
   329  			So(host.Status, ShouldEqual, evergreen.HostRunning)
   330  			So(host.Provisioned, ShouldEqual, true)
   331  
   332  			host, err = FindOne(ById(host.Id))
   333  			So(err, ShouldBeNil)
   334  			So(host.Status, ShouldEqual, evergreen.HostRunning)
   335  			So(host.Provisioned, ShouldEqual, true)
   336  
   337  		})
   338  
   339  	})
   340  }
   341  
   342  func TestHostCreateSecret(t *testing.T) {
   343  	Convey("With a host with no secret", t, func() {
   344  
   345  		testutil.HandleTestingErr(db.Clear(Collection), t,
   346  			"Error clearing '%v' collection", Collection)
   347  
   348  		host := &Host{Id: "hostOne"}
   349  		So(host.Insert(), ShouldBeNil)
   350  
   351  		Convey("creating a secret", func() {
   352  			So(host.Secret, ShouldEqual, "")
   353  			So(host.CreateSecret(), ShouldBeNil)
   354  
   355  			Convey("should update the host in memory", func() {
   356  				So(host.Secret, ShouldNotEqual, "")
   357  
   358  				Convey("and in the database", func() {
   359  					dbHost, err := FindOne(ById(host.Id))
   360  					So(err, ShouldBeNil)
   361  					So(dbHost.Secret, ShouldEqual, host.Secret)
   362  				})
   363  			})
   364  		})
   365  	})
   366  }
   367  
   368  func TestHostSetExpirationTime(t *testing.T) {
   369  
   370  	Convey("With a host", t, func() {
   371  
   372  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   373  			" clearing '%v' collection", Collection)
   374  
   375  		initialExpirationTime := time.Now()
   376  		notifications := make(map[string]bool)
   377  		notifications["2h"] = true
   378  
   379  		memHost := &Host{
   380  			Id:             "hostOne",
   381  			ExpirationTime: initialExpirationTime,
   382  			Notifications:  notifications,
   383  		}
   384  		So(memHost.Insert(), ShouldBeNil)
   385  
   386  		Convey("setting the expiration time for the host should change the "+
   387  			" expiration time for both the in-memory and database"+
   388  			" copies of the host and unset the notifications", func() {
   389  
   390  			dbHost, err := FindOne(ById(memHost.Id))
   391  
   392  			// ensure the db entries are as expected
   393  			So(err, ShouldBeNil)
   394  			So(memHost.ExpirationTime.Round(time.Second).Equal(
   395  				initialExpirationTime.Round(time.Second)), ShouldBeTrue)
   396  			So(dbHost.ExpirationTime.Round(time.Second).Equal(
   397  				initialExpirationTime.Round(time.Second)), ShouldBeTrue)
   398  			So(memHost.Notifications, ShouldResemble, notifications)
   399  			So(dbHost.Notifications, ShouldResemble, notifications)
   400  
   401  			// now update the expiration time
   402  			newExpirationTime := time.Now()
   403  			So(memHost.SetExpirationTime(newExpirationTime), ShouldBeNil)
   404  
   405  			dbHost, err = FindOne(ById(memHost.Id))
   406  
   407  			// ensure the db entries are as expected
   408  			So(err, ShouldBeNil)
   409  			So(memHost.ExpirationTime.Round(time.Second).Equal(
   410  				newExpirationTime.Round(time.Second)), ShouldBeTrue)
   411  			So(dbHost.ExpirationTime.Round(time.Second).Equal(
   412  				newExpirationTime.Round(time.Second)), ShouldBeTrue)
   413  			So(memHost.Notifications, ShouldResemble, make(map[string]bool))
   414  			So(dbHost.Notifications, ShouldEqual, nil)
   415  		})
   416  	})
   417  }
   418  
   419  func TestFindRunningSpawnedHosts(t *testing.T) {
   420  	testConfig := testutil.TestConfig()
   421  	db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testConfig))
   422  
   423  	testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   424  		" clearing '%v' collection", Collection)
   425  
   426  	Convey("With calling FindRunningSpawnedHosts...", t, func() {
   427  		Convey("if there are no spawned hosts, nothing should be returned",
   428  			func() {
   429  				spawnedHosts, err := Find(IsRunningAndSpawned)
   430  				So(err, ShouldBeNil)
   431  				// make sure we only returned no document
   432  				So(len(spawnedHosts), ShouldEqual, 0)
   433  
   434  			})
   435  
   436  		Convey("if there are spawned hosts, they should be returned", func() {
   437  			host := &Host{}
   438  			host.Id = "spawned-1"
   439  			host.Status = "running"
   440  			host.StartedBy = "user1"
   441  			testutil.HandleTestingErr(host.Insert(), t, "error from "+
   442  				"FindRunningSpawnedHosts")
   443  			spawnedHosts, err := Find(IsRunningAndSpawned)
   444  			testutil.HandleTestingErr(err, t, "error from "+
   445  				"FindRunningSpawnedHosts: %v", err)
   446  			// make sure we only returned no document
   447  			So(len(spawnedHosts), ShouldEqual, 1)
   448  
   449  		})
   450  	})
   451  }
   452  func TestSetExpirationNotification(t *testing.T) {
   453  
   454  	Convey("With a host", t, func() {
   455  
   456  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   457  			" clearing '%v' collection", Collection)
   458  
   459  		notifications := make(map[string]bool)
   460  		notifications["2h"] = true
   461  
   462  		memHost := &Host{
   463  			Id:            "hostOne",
   464  			Notifications: notifications,
   465  		}
   466  		So(memHost.Insert(), ShouldBeNil)
   467  
   468  		Convey("setting the expiration notification for the host should change "+
   469  			" the expiration notification for both the in-memory and database"+
   470  			" copies of the host and unset the notifications", func() {
   471  
   472  			dbHost, err := FindOne(ById(memHost.Id))
   473  
   474  			// ensure the db entries are as expected
   475  			So(err, ShouldBeNil)
   476  			So(memHost.Notifications, ShouldResemble, notifications)
   477  			So(dbHost.Notifications, ShouldResemble, notifications)
   478  
   479  			// now update the expiration notification
   480  			notifications["4h"] = true
   481  			So(memHost.SetExpirationNotification("4h"), ShouldBeNil)
   482  			dbHost, err = FindOne(ById(memHost.Id))
   483  			// ensure the db entries are as expected
   484  			So(err, ShouldBeNil)
   485  			So(memHost.Notifications, ShouldResemble, notifications)
   486  			So(dbHost.Notifications, ShouldResemble, notifications)
   487  		})
   488  	})
   489  }
   490  
   491  func TestHostClearRunningTask(t *testing.T) {
   492  
   493  	Convey("With a host", t, func() {
   494  
   495  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   496  			" clearing '%v' collection", Collection)
   497  
   498  		var err error
   499  		var count int
   500  
   501  		host := &Host{
   502  			Id:          "hostOne",
   503  			RunningTask: "taskId",
   504  			StartedBy:   evergreen.User,
   505  			Status:      evergreen.HostRunning,
   506  			Pid:         "12345",
   507  		}
   508  
   509  		So(host.Insert(), ShouldBeNil)
   510  
   511  		Convey("host statistics should properly count this host as active"+
   512  			" but not idle", func() {
   513  			count, err = Count(IsActive)
   514  			So(err, ShouldBeNil)
   515  			So(count, ShouldEqual, 1)
   516  			count, err = Count(IsIdle)
   517  			So(err, ShouldBeNil)
   518  			So(count, ShouldEqual, 0)
   519  		})
   520  
   521  		Convey("clearing the running task should clear the running task, pid,"+
   522  			" and task dispatch time fields from both the in-memory and"+
   523  			" database copies of the host", func() {
   524  
   525  			So(host.ClearRunningTask("prevTask", time.Now()), ShouldBeNil)
   526  			So(host.RunningTask, ShouldEqual, "")
   527  			So(host.LastTaskCompleted, ShouldEqual, "prevTask")
   528  
   529  			host, err = FindOne(ById(host.Id))
   530  			So(err, ShouldBeNil)
   531  
   532  			So(host.RunningTask, ShouldEqual, "")
   533  			So(host.LastTaskCompleted, ShouldEqual, "prevTask")
   534  
   535  			Convey("the count of idle hosts should go up", func() {
   536  				count, err := Count(IsIdle)
   537  				So(err, ShouldBeNil)
   538  				So(count, ShouldEqual, 1)
   539  
   540  				Convey("but the active host count should remain the same", func() {
   541  					count, err = Count(IsActive)
   542  					So(err, ShouldBeNil)
   543  					So(count, ShouldEqual, 1)
   544  				})
   545  			})
   546  
   547  		})
   548  
   549  	})
   550  }
   551  
   552  func TestUpdateHostRunningTask(t *testing.T) {
   553  	Convey("With a host", t, func() {
   554  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   555  			" clearing '%v' collection", Collection)
   556  		oldTaskId := "oldId"
   557  		newTaskId := "newId"
   558  		h := Host{
   559  			Id:          "test",
   560  			RunningTask: oldTaskId,
   561  			Status:      evergreen.HostRunning,
   562  		}
   563  		So(h.Insert(), ShouldBeNil)
   564  		Convey("updating the running task id should set proper fields", func() {
   565  			_, err := h.UpdateRunningTask(oldTaskId, newTaskId, time.Now())
   566  			So(err, ShouldBeNil)
   567  			found, err := FindOne(ById(h.Id))
   568  			So(err, ShouldBeNil)
   569  			So(found.RunningTask, ShouldEqual, newTaskId)
   570  			So(found.LastTaskCompleted, ShouldEqual, oldTaskId)
   571  			runningTaskHosts, err := Find(IsRunningTask)
   572  			So(err, ShouldBeNil)
   573  			So(len(runningTaskHosts), ShouldEqual, 1)
   574  		})
   575  		Convey("updating the running task to an empty string should error out", func() {
   576  			_, err := h.UpdateRunningTask(newTaskId, "", time.Now())
   577  			So(err, ShouldNotBeNil)
   578  		})
   579  	})
   580  }
   581  
   582  func TestUpsert(t *testing.T) {
   583  
   584  	Convey("With a host", t, func() {
   585  
   586  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   587  			" clearing '%v' collection", Collection)
   588  
   589  		host := &Host{
   590  			Id:     "hostOne",
   591  			Host:   "host",
   592  			User:   "user",
   593  			Distro: distro.Distro{Id: "distro"},
   594  			Status: evergreen.HostRunning,
   595  		}
   596  
   597  		var err error
   598  
   599  		Convey("Performing a host upsert should upsert correctly", func() {
   600  			_, err = host.Upsert()
   601  			So(err, ShouldBeNil)
   602  			So(host.Status, ShouldEqual, evergreen.HostRunning)
   603  
   604  			host, err = FindOne(ById(host.Id))
   605  			So(err, ShouldBeNil)
   606  			So(host.Status, ShouldEqual, evergreen.HostRunning)
   607  
   608  		})
   609  
   610  		Convey("Updating some fields of an already inserted host should cause "+
   611  			"those fields to be updated but should leave status unchanged",
   612  			func() {
   613  				_, err := host.Upsert()
   614  				So(err, ShouldBeNil)
   615  				So(host.Status, ShouldEqual, evergreen.HostRunning)
   616  
   617  				host, err = FindOne(ById(host.Id))
   618  				So(err, ShouldBeNil)
   619  				So(host.Status, ShouldEqual, evergreen.HostRunning)
   620  				So(host.Host, ShouldEqual, "host")
   621  
   622  				err = UpdateOne(
   623  					bson.M{
   624  						IdKey: host.Id,
   625  					},
   626  					bson.M{
   627  						"$set": bson.M{
   628  							StatusKey: evergreen.HostDecommissioned,
   629  						},
   630  					},
   631  				)
   632  				So(err, ShouldBeNil)
   633  
   634  				// update the hostname and status
   635  				host.Host = "host2"
   636  				host.Status = evergreen.HostRunning
   637  				_, err = host.Upsert()
   638  				So(err, ShouldBeNil)
   639  
   640  				// host db status should remain unchanged
   641  				host, err = FindOne(ById(host.Id))
   642  				So(err, ShouldBeNil)
   643  				So(host.Status, ShouldEqual, evergreen.HostDecommissioned)
   644  				So(host.Host, ShouldEqual, "host2")
   645  
   646  			})
   647  	})
   648  }
   649  
   650  func TestDecommissionHostsWithDistroId(t *testing.T) {
   651  
   652  	Convey("With a multiple hosts of different distros", t, func() {
   653  
   654  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   655  			" clearing '%v' collection", Collection)
   656  
   657  		distroA := "distro_a"
   658  		distroB := "distro_b"
   659  
   660  		// Insert 10 of distro a and 10 of distro b
   661  
   662  		for i := 0; i < 10; i++ {
   663  			hostWithDistroA := &Host{
   664  				Id:     fmt.Sprintf("hostA%v", i),
   665  				Host:   "host",
   666  				User:   "user",
   667  				Distro: distro.Distro{Id: distroA},
   668  				Status: evergreen.HostRunning,
   669  			}
   670  			hostWithDistroB := &Host{
   671  				Id:     fmt.Sprintf("hostB%v", i),
   672  				Host:   "host",
   673  				User:   "user",
   674  				Distro: distro.Distro{Id: distroB},
   675  				Status: evergreen.HostRunning,
   676  			}
   677  
   678  			testutil.HandleTestingErr(hostWithDistroA.Insert(), t, "Error inserting"+
   679  				"host into database")
   680  			testutil.HandleTestingErr(hostWithDistroB.Insert(), t, "Error inserting"+
   681  				"host into database")
   682  		}
   683  
   684  		Convey("When decommissioning hosts of type distro_a", func() {
   685  			err := DecommissionHostsWithDistroId(distroA)
   686  			So(err, ShouldBeNil)
   687  
   688  			Convey("Distro should be marked as decommissioned accordingly", func() {
   689  				hostsTypeA, err := Find(ByDistroId(distroA))
   690  				So(err, ShouldBeNil)
   691  
   692  				hostsTypeB, err := Find(ByDistroId(distroB))
   693  				So(err, ShouldBeNil)
   694  				for _, host := range hostsTypeA {
   695  
   696  					So(host.Status, ShouldEqual, evergreen.HostDecommissioned)
   697  				}
   698  
   699  				for _, host := range hostsTypeB {
   700  					So(host.Status, ShouldEqual, evergreen.HostRunning)
   701  
   702  				}
   703  			})
   704  
   705  		})
   706  
   707  	})
   708  }
   709  
   710  func TestFindByLCT(t *testing.T) {
   711  	Convey("with the a given time for checking and an empty hosts collection", t, func() {
   712  		testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+
   713  			" clearing '%v' collection", Collection)
   714  		now := time.Now()
   715  		Convey("with a host that has no last communication time", func() {
   716  			h := Host{
   717  				Id:        "id",
   718  				Status:    evergreen.HostRunning,
   719  				StartedBy: evergreen.User,
   720  			}
   721  			So(h.Insert(), ShouldBeNil)
   722  			hosts, err := Find(ByRunningWithTimedOutLCT(time.Now()))
   723  			So(err, ShouldBeNil)
   724  			So(len(hosts), ShouldEqual, 1)
   725  			So(hosts[0].Id, ShouldEqual, "id")
   726  			Convey("after unsetting the host's lct", func() {
   727  				err := UpdateOne(bson.M{IdKey: h.Id},
   728  					bson.M{
   729  						"$unset": bson.M{LastCommunicationTimeKey: 0},
   730  					})
   731  				So(err, ShouldBeNil)
   732  				foundHost, err := FindOne(ById(h.Id))
   733  				So(err, ShouldBeNil)
   734  				So(foundHost, ShouldNotBeNil)
   735  				hosts, err := Find(ByRunningWithTimedOutLCT(time.Now()))
   736  				So(err, ShouldBeNil)
   737  				So(len(hosts), ShouldEqual, 1)
   738  				So(hosts[0].Id, ShouldEqual, h.Id)
   739  			})
   740  		})
   741  
   742  		Convey("with a host with a last communication time > 10 mins", func() {
   743  			anotherHost := Host{
   744  				Id: "anotherID",
   745  				LastCommunicationTime: now.Add(-time.Duration(20) * time.Minute),
   746  				Status:                evergreen.HostRunning,
   747  				StartedBy:             evergreen.User,
   748  			}
   749  			So(anotherHost.Insert(), ShouldBeNil)
   750  			hosts, err := Find(ByRunningWithTimedOutLCT(now))
   751  			So(err, ShouldBeNil)
   752  			So(len(hosts), ShouldEqual, 1)
   753  			So(hosts[0].Id, ShouldEqual, anotherHost.Id)
   754  		})
   755  
   756  		Convey("with a host with a normal LCT", func() {
   757  			anotherHost := Host{
   758  				Id: "testhost",
   759  				LastCommunicationTime: now.Add(time.Duration(5) * time.Minute),
   760  				Status:                evergreen.HostRunning,
   761  				StartedBy:             evergreen.User,
   762  			}
   763  			So(anotherHost.Insert(), ShouldBeNil)
   764  			hosts, err := Find(ByRunningWithTimedOutLCT(now))
   765  			So(err, ShouldBeNil)
   766  			So(len(hosts), ShouldEqual, 0)
   767  			Convey("after resetting the LCT", func() {
   768  				So(anotherHost.ResetLastCommunicated(), ShouldBeNil)
   769  				So(anotherHost.LastCommunicationTime, ShouldResemble, time.Unix(0, 0))
   770  				h, err := Find(ByRunningWithTimedOutLCT(now))
   771  				So(err, ShouldBeNil)
   772  				So(len(h), ShouldEqual, 1)
   773  				So(h[0].Id, ShouldEqual, "testhost")
   774  			})
   775  		})
   776  		Convey("with a terminated host that has no LCT", func() {
   777  			h := Host{
   778  				Id:        "h",
   779  				Status:    evergreen.HostTerminated,
   780  				StartedBy: evergreen.User,
   781  			}
   782  			So(h.Insert(), ShouldBeNil)
   783  			hosts, err := Find(ByRunningWithTimedOutLCT(now))
   784  			So(err, ShouldBeNil)
   785  			So(len(hosts), ShouldEqual, 0)
   786  		})
   787  		Convey("with a host with that does not have a user", func() {
   788  			h := Host{
   789  				Id:        "h",
   790  				Status:    evergreen.HostRunning,
   791  				StartedBy: "anotherUser",
   792  			}
   793  			So(h.Insert(), ShouldBeNil)
   794  			hosts, err := Find(ByRunningWithTimedOutLCT(now))
   795  			So(err, ShouldBeNil)
   796  			So(len(hosts), ShouldEqual, 0)
   797  
   798  		})
   799  
   800  	})
   801  }