github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/agent/api_integration_test.go (about) 1 package agent 2 3 import ( 4 "crypto/md5" 5 "crypto/tls" 6 "encoding/hex" 7 "fmt" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/evergreen-ci/evergreen" 16 "github.com/evergreen-ci/evergreen/agent/comm" 17 "github.com/evergreen-ci/evergreen/apimodels" 18 "github.com/evergreen-ci/evergreen/command" 19 dbutil "github.com/evergreen-ci/evergreen/db" 20 "github.com/evergreen-ci/evergreen/model" 21 "github.com/evergreen-ci/evergreen/model/build" 22 "github.com/evergreen-ci/evergreen/model/host" 23 "github.com/evergreen-ci/evergreen/model/manifest" 24 "github.com/evergreen-ci/evergreen/model/patch" 25 "github.com/evergreen-ci/evergreen/model/task" 26 modelutil "github.com/evergreen-ci/evergreen/model/testutil" 27 "github.com/evergreen-ci/evergreen/model/version" 28 "github.com/evergreen-ci/evergreen/plugin" 29 _ "github.com/evergreen-ci/evergreen/plugin/config" 30 "github.com/evergreen-ci/evergreen/service" 31 "github.com/evergreen-ci/evergreen/testutil" 32 "github.com/mongodb/grip" 33 "github.com/mongodb/grip/level" 34 "github.com/mongodb/grip/slogger" 35 "github.com/pkg/errors" 36 . "github.com/smartystreets/goconvey/convey" 37 "github.com/smartystreets/goconvey/convey/reporting" 38 "gopkg.in/mgo.v2/bson" 39 ) 40 41 var testConfig = testutil.TestConfig() 42 43 var testSetups []testConfigPath 44 45 var buildVariantsToTest = []string{"linux-64", "windows8"} 46 47 var tlsConfigs map[string]*tls.Config 48 49 var testDirectory string 50 51 // NoopSignal is a signal handler that ignores all signals, so that we can 52 // intercept and check for them directly in the test instead 53 type NoopSignalHandler struct{} 54 55 type testConfigPath struct { 56 testSpec string 57 } 58 59 func init() { 60 testDirectory = testutil.GetDirectoryOfFile() 61 62 dbutil.SetGlobalSessionProvider(dbutil.SessionFactoryFromConfig(testConfig)) 63 testSetups = []testConfigPath{ 64 {"With plugin mode test config"}, 65 } 66 reporting.QuietMode() 67 grip.SetThreshold(level.Debug) 68 } 69 70 func (*NoopSignalHandler) HandleSignals(_ *Agent) { 71 return 72 } 73 74 func setupTlsConfigs(t *testing.T) { 75 if tlsConfigs == nil { 76 tlsConfig := &tls.Config{} 77 tlsConfig.NextProtos = []string{"http/1.1"} 78 79 var err error 80 tlsConfig.Certificates = make([]tls.Certificate, 1) 81 tlsConfig.Certificates[0], err = 82 tls.X509KeyPair([]byte(testConfig.Api.HttpsCert), 83 []byte(testConfig.Api.HttpsKey)) 84 if err != nil { 85 testutil.HandleTestingErr(err, t, "X509KeyPair failed during test initialization: %v", err) 86 } 87 tlsConfigs = map[string]*tls.Config{ 88 "http": nil, 89 // TODO: do tests over SSL, see EVG-1512 90 // "https": tlsConfig, 91 } 92 } 93 } 94 95 func createAgent(testServer *service.TestServer, testHost *host.Host) (*Agent, error) { 96 testAgent, err := New(Options{ 97 APIURL: testServer.URL, 98 HostId: testHost.Id, 99 HostSecret: testHost.Secret, 100 Certificate: testConfig.Api.HttpsCert, 101 }) 102 103 if err != nil { 104 return nil, err 105 } 106 107 testAgent.heartbeater.Interval = 10 * time.Second 108 testAgent.taskConfig = &model.TaskConfig{Expansions: &command.Expansions{}} 109 110 if err := testAgent.Setup(); err != nil { 111 return nil, err 112 } 113 114 return testAgent, nil 115 } 116 117 func assignAgentTask(agt *Agent, testTask *task.Task) { 118 agt.SetTask(testTask.Id, testTask.Secret) 119 } 120 121 func TestBasicEndpoints(t *testing.T) { 122 setupTlsConfigs(t) 123 for tlsString, tlsConfig := range tlsConfigs { 124 modelData, err := modelutil.SetupAPITestData(testConfig, "task", "linux-64", filepath.Join(testDirectory, "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 125 testutil.HandleTestingErr(err, t, "Couldn't make test data: %v", err) 126 127 Convey("With a live api server, agent, and test task over "+tlsString, t, func() { 128 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 129 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 130 defer testServer.Close() 131 132 testAgent, err := createAgent(testServer, modelData.Host) 133 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 134 135 assignAgentTask(testAgent, modelData.Task) 136 137 Convey("sending logs should store the log messages properly", func() { 138 msg1 := "task logger initialized!" 139 msg2 := "system logger initialized!" 140 msg3 := "exec logger initialized!" 141 testAgent.logger.LogTask(slogger.INFO, msg1) 142 testAgent.logger.LogSystem(slogger.INFO, msg2) 143 testAgent.logger.LogExecution(slogger.INFO, msg3) 144 time.Sleep(1 * time.Second) 145 testAgent.APILogger.FlushAndWait() 146 147 // This returns logs in order of NEWEST first. 148 logMessages, err := model.FindMostRecentLogMessages(modelData.Task.Id, 0, 10, []string{}, []string{}) 149 testutil.HandleTestingErr(err, t, "failed to get log msgs") 150 So(logMessages[2].Message, ShouldEndWith, msg1) 151 So(logMessages[1].Message, ShouldEndWith, msg2) 152 So(logMessages[0].Message, ShouldEndWith, msg3) 153 Convey("Task endpoints should work between agent and server", func() { 154 testAgent.StartBackgroundActions(&NoopSignalHandler{}) 155 Convey("calling GetTask should get retrieve same task back", func() { 156 var testTaskFromApi *task.Task 157 testTaskFromApi, err = testAgent.GetTask() 158 So(err, ShouldBeNil) 159 160 // ShouldResemble doesn't seem to work here, possibly because of 161 // omitempty? anyways, just assert equality of the important fields 162 So(testTaskFromApi.Id, ShouldEqual, modelData.Task.Id) 163 So(testTaskFromApi.Status, ShouldEqual, modelData.Task.Status) 164 So(testTaskFromApi.HostId, ShouldEqual, modelData.Task.HostId) 165 }) 166 167 Convey("calling start should flip the task's status to started", func() { 168 var testHost *host.Host 169 var testTask *task.Task 170 err = testAgent.Start() 171 testutil.HandleTestingErr(err, t, "Couldn't start task: %v", err) 172 173 testTask, err = task.FindOne(task.ById(modelData.Task.Id)) 174 testutil.HandleTestingErr(err, t, "Couldn't refresh task from db: %v", err) 175 So(testTask.Status, ShouldEqual, evergreen.TaskStarted) 176 testHost, err = host.FindOne(host.ByRunningTaskId(testTask.Id)) 177 So(err, ShouldBeNil) 178 So(testHost.Id, ShouldEqual, "testHost") 179 So(testHost.RunningTask, ShouldEqual, testTask.Id) 180 }) 181 182 Convey("calling end() should update task status properly", func() { 183 commandType := model.SystemCommandType 184 description := "random" 185 details := &apimodels.TaskEndDetail{ 186 Description: description, 187 Type: commandType, 188 TimedOut: true, 189 Status: evergreen.TaskSucceeded, 190 } 191 _, err = testAgent.End(details) 192 So(err, ShouldBeNil) 193 time.Sleep(100 * time.Millisecond) 194 taskUpdate, err := task.FindOne(task.ById(modelData.Task.Id)) 195 So(err, ShouldBeNil) 196 So(taskUpdate.Status, ShouldEqual, evergreen.TaskSucceeded) 197 So(taskUpdate.Details.Description, ShouldEqual, description) 198 So(taskUpdate.Details.Type, ShouldEqual, commandType) 199 So(taskUpdate.Details.TimedOut, ShouldEqual, true) 200 }) 201 202 Convey("no checkins should trigger timeout signal", func() { 203 testAgent.idleTimeoutWatcher.SetDuration(2 * time.Second) 204 testAgent.idleTimeoutWatcher.CheckIn() 205 // sleep long enough for the timeout watcher to time out 206 time.Sleep(3 * time.Second) 207 timeoutSignal, ok := <-testAgent.signalHandler.idleTimeoutChan 208 So(ok, ShouldBeTrue) 209 So(timeoutSignal, ShouldEqual, comm.IdleTimeout) 210 }) 211 }) 212 }) 213 }) 214 } 215 } 216 217 func TestHeartbeatSignals(t *testing.T) { 218 setupTlsConfigs(t) 219 220 for tlsString, tlsConfig := range tlsConfigs { 221 modelData, err := modelutil.SetupAPITestData(testConfig, evergreen.CompileStage, "linux-64", filepath.Join(testDirectory, "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 222 testutil.HandleTestingErr(err, t, "Couldn't make test data: %v", err) 223 224 Convey("With a live api server, agent, and test task over "+tlsString, t, func() { 225 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 226 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 227 defer testServer.Close() 228 testAgent, err := createAgent(testServer, modelData.Host) 229 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 230 231 testAgent.heartbeater.Interval = 100 * time.Millisecond 232 testAgent.StartBackgroundActions(&NoopSignalHandler{}) 233 234 Convey("killing the server should result in failure signal", func() { 235 So(testServer.Listener.Close(), ShouldBeNil) 236 signal, ok := <-testAgent.signalHandler.heartbeatChan 237 So(ok, ShouldBeTrue) 238 So(signal, ShouldEqual, comm.HeartbeatMaxFailed) 239 }) 240 }) 241 } 242 } 243 244 func TestAgentDirectorySuccess(t *testing.T) { 245 setupTlsConfigs(t) 246 for tlsString, tlsConfig := range tlsConfigs { 247 Convey("With agent printing directory and live API server over "+tlsString, t, func() { 248 249 modelData, err := modelutil.SetupAPITestData(testConfig, "print_dir_task", "linux-64", filepath.Join(testDirectory, 250 "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 251 testutil.HandleTestingErr(err, t, "Failed to find test data") 252 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 253 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 254 defer testServer.Close() 255 256 testAgent, err := createAgent(testServer, modelData.Host) 257 testutil.HandleTestingErr(err, t, "Failed to start agent") 258 259 assignAgentTask(testAgent, modelData.Task) 260 261 So(err, ShouldBeNil) 262 So(testAgent, ShouldNotBeNil) 263 264 dir, err := os.Getwd() 265 266 testutil.HandleTestingErr(err, t, "Failed to read current directory") 267 268 distro, err := testAgent.GetDistro() 269 testutil.HandleTestingErr(err, t, "Failed to get agent distro") 270 _, err = testAgent.RunTask() 271 So(err, ShouldBeNil) 272 273 printLogsForTask(modelData.Task.Id) 274 275 h := md5.New() 276 _, err = h.Write([]byte(fmt.Sprintf("%s_%d_%d", modelData.Task.Id, 0, os.Getpid()))) 277 So(err, ShouldBeNil) 278 279 dirName := hex.EncodeToString(h.Sum(nil)) 280 newDir := filepath.Join(distro.WorkDir, dirName) 281 282 Convey("Then the directory should have been set and printed", func() { 283 So(scanLogsForTask(modelData.Task.Id, "", "printing current directory"), ShouldBeTrue) 284 So(scanLogsForTask(modelData.Task.Id, "", newDir), ShouldBeTrue) 285 }) 286 Convey("Then the directory should have been deleted", func() { 287 var files []os.FileInfo 288 files, err = ioutil.ReadDir("./") 289 testutil.HandleTestingErr(err, t, "Failed to read current directory") 290 for _, f := range files { 291 So(f.Name(), ShouldNotEqual, newDir) 292 } 293 }) 294 err = os.Chdir(dir) 295 testutil.HandleTestingErr(err, t, "Failed to change directory back to main dir") 296 }) 297 } 298 } 299 func TestAgentDirectoryFailure(t *testing.T) { 300 setupTlsConfigs(t) 301 for tlsString, tlsConfig := range tlsConfigs { 302 Convey("With agent printing directory and live API server over "+tlsString, t, func() { 303 modelData, err := modelutil.SetupAPITestData(testConfig, "print_dir_task", "linux-64", 304 filepath.Join(testDirectory, "testdata", "config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 305 testutil.HandleTestingErr(err, t, "Failed to find test task") 306 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 307 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 308 defer testServer.Close() 309 testAgent, err := createAgent(testServer, modelData.Host) 310 testutil.HandleTestingErr(err, t, "Failed to start test agent") 311 312 assignAgentTask(testAgent, modelData.Task) 313 dir, err := os.Getwd() 314 315 testutil.HandleTestingErr(err, t, "Failed to read current directory") 316 317 distro, err := testAgent.GetDistro() 318 testutil.HandleTestingErr(err, t, "Failed to get agent distro") 319 320 h := md5.New() 321 322 _, err = h.Write([]byte(fmt.Sprintf("%s_%d_%d", modelData.Task.Id, 0, 323 os.Getpid()))) 324 So(err, ShouldBeNil) 325 dirName := hex.EncodeToString(h.Sum(nil)) 326 newDir := filepath.Join(distro.WorkDir, dirName) 327 328 newDirFile, err := os.Create(newDir) 329 testutil.HandleTestingErr(err, t, "Couldn't create file: %v", err) 330 331 _, err = testAgent.RunTask() 332 Convey("Then the agent should have errored", func() { 333 So(err, ShouldNotBeNil) 334 }) 335 336 printLogsForTask(modelData.Task.Id) 337 Convey("Then the task should not have been run", func() { 338 So(scanLogsForTask(modelData.Task.Id, "", "printing current directory"), ShouldBeFalse) 339 So(scanLogsForTask(modelData.Task.Id, "", newDir), ShouldBeFalse) 340 }) 341 <-testAgent.KillChan 342 Convey("Then the taskDetail type should have been set to SystemCommandType and have status failed", func() { 343 select { 344 case detail := <-testAgent.endChan: 345 So(detail.Type, ShouldEqual, model.SystemCommandType) 346 So(detail.Status, ShouldEqual, evergreen.TaskFailed) 347 default: 348 t.Errorf("unable to read from the endChan") 349 } 350 }) 351 err = os.Chdir(dir) 352 testutil.HandleTestingErr(err, t, "Failed to change directory back to main dir") 353 354 testutil.HandleTestingErr(newDirFile.Close(), t, "failed to close dummy directory, file") 355 err = os.Remove(newDir) 356 testutil.HandleTestingErr(err, t, "Failed to remove dummy directory file") 357 }) 358 } 359 } 360 361 func TestSecrets(t *testing.T) { 362 setupTlsConfigs(t) 363 modelData, err := modelutil.SetupAPITestData(testConfig, evergreen.CompileStage, "linux-64", filepath.Join(testDirectory, "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 364 testutil.HandleTestingErr(err, t, "Couldn't make test data: %v", err) 365 366 for tlsString, tlsConfig := range tlsConfigs { 367 Convey("With a live api server, agent, and test task over "+tlsString, t, func() { 368 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 369 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 370 defer testServer.Close() 371 testAgent, err := createAgent(testServer, modelData.Host) 372 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 373 374 assignAgentTask(testAgent, modelData.Task) 375 376 testAgent.heartbeater.Interval = 100 * time.Millisecond 377 testAgent.StartBackgroundActions(&NoopSignalHandler{}) 378 379 Convey("killing the server should result in failure signal", func() { 380 So(testServer.Listener.Close(), ShouldBeNil) 381 signal, ok := <-testAgent.signalHandler.heartbeatChan 382 So(ok, ShouldBeTrue) 383 So(signal, ShouldEqual, comm.HeartbeatMaxFailed) 384 }) 385 }) 386 } 387 } 388 389 func TestTaskSuccess(t *testing.T) { 390 setupTlsConfigs(t) 391 testutil.ConfigureIntegrationTest(t, testConfig, "TestTaskSuccess") 392 393 for tlsString, tlsConfig := range tlsConfigs { 394 for _, testSetup := range testSetups { 395 for _, variant := range buildVariantsToTest { 396 Convey(testSetup.testSpec, t, func() { 397 Convey("With agent running 'compile' step and live API server over "+ 398 tlsString+" with variant "+variant, func() { 399 modelData, err := modelutil.SetupAPITestData(testConfig, "compile", variant, filepath.Join(testDirectory, "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 400 testutil.HandleTestingErr(err, t, "Couldn't create test task: %v", err) 401 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 402 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 403 defer testServer.Close() 404 testAgent, err := createAgent(testServer, modelData.Host) 405 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 406 407 assignAgentTask(testAgent, modelData.Task) 408 409 // actually run the task. 410 // this function won't return until the whole thing is done. 411 _, err = testAgent.RunTask() 412 So(err, ShouldBeNil) 413 Convey("expansions should be fetched", func() { 414 So(testAgent.taskConfig.Expansions.Get("aws_key"), ShouldEqual, testConfig.Providers.AWS.Id) 415 So(testAgent.taskConfig.Expansions.Get("distro_id"), ShouldEqual, testAgent.taskConfig.Distro.Id) 416 So(testAgent.taskConfig.Expansions.Get("distro_id"), ShouldEqual, "test-distro-one") 417 So(scanLogsForTask(modelData.Task.Id, "", "fetch_expansion_value"), ShouldBeTrue) 418 }) 419 time.Sleep(100 * time.Millisecond) 420 testAgent.APILogger.FlushAndWait() 421 printLogsForTask(modelData.Task.Id) 422 423 Convey("all scripts in task should have been run successfully", func() { 424 So(scanLogsForTask(modelData.Task.Id, "", "Executing script with sh: echo \"predefined command!\""), ShouldBeTrue) 425 So(scanLogsForTask(modelData.Task.Id, "", "executing the pre-run script"), ShouldBeTrue) 426 So(scanLogsForTask(modelData.Task.Id, "", "executing the post-run script!"), ShouldBeTrue) 427 So(scanLogsForTask(modelData.Task.Id, "", "predefined command!"), ShouldBeTrue) 428 So(scanLogsForTask(modelData.Task.Id, "", "this should not end up in the logs"), ShouldBeFalse) 429 So(scanLogsForTask(modelData.Task.Id, "", "Command timeout set to 21m40s"), ShouldBeTrue) 430 So(scanLogsForTask(modelData.Task.Id, "", "Command timeout set to 43m20s"), ShouldBeTrue) 431 So(scanLogsForTask(modelData.Task.Id, "", "Cloning into") || // git 1.8 432 scanLogsForTask(modelData.Task.Id, "", "Initialized empty Git repository"), // git 1.7 433 ShouldBeTrue) 434 So(scanLogsForTask(modelData.Task.Id, "", "i am compiling!"), ShouldBeTrue) 435 So(scanLogsForTask(modelData.Task.Id, "", "i am sanity testing!"), ShouldBeTrue) 436 437 // Check that functions with args are working correctly 438 So(scanLogsForTask(modelData.Task.Id, "", "arg1 is FOO"), ShouldBeTrue) 439 So(scanLogsForTask(modelData.Task.Id, "", "arg2 is BAR"), ShouldBeTrue) 440 So(scanLogsForTask(modelData.Task.Id, "", "arg3 is Expanded: qux"), ShouldBeTrue) 441 So(scanLogsForTask(modelData.Task.Id, "", "arg4 is Default: default_value"), ShouldBeTrue) 442 443 // Check that multi-command functions are working correctly 444 So(scanLogsForTask(modelData.Task.Id, "", "step 1 of multi-command func"), ShouldBeTrue) 445 So(scanLogsForTask(modelData.Task.Id, "", "step 2 of multi-command func"), ShouldBeTrue) 446 So(scanLogsForTask(modelData.Task.Id, "", "step 3 of multi-command func"), ShouldBeTrue) 447 448 testTask, err := task.FindOne(task.ById(modelData.Task.Id)) 449 testutil.HandleTestingErr(err, t, "Couldn't find test task: %v", err) 450 So(testTask.Status, ShouldEqual, evergreen.TaskSucceeded) 451 452 // use function display name as description when none is specified in command 453 So(testTask.Details.Status, ShouldEqual, evergreen.TaskSucceeded) 454 So(testTask.Details.Description, ShouldEqual, `'shell.exec' in "silent shell test"`) 455 So(testTask.Details.TimedOut, ShouldBeFalse) 456 So(testTask.Details.Type, ShouldEqual, model.SystemCommandType) 457 }) 458 459 Convey("manifest should be created", func() { 460 m, err := manifest.FindOne(manifest.ById(modelData.Task.Version)) 461 So(modelData.Task.Version, ShouldEqual, "testVersionId") 462 So(err, ShouldBeNil) 463 So(m, ShouldNotBeNil) 464 So(m.ProjectName, ShouldEqual, modelData.Task.Project) 465 So(m.Id, ShouldEqual, modelData.Task.Version) 466 So(m.Modules, ShouldNotBeEmpty) 467 So(m.Modules["recursive"], ShouldNotBeNil) 468 So(m.Modules["recursive"].URL, ShouldNotEqual, "") 469 }) 470 }) 471 472 Convey("With agent running a regular test and live API server over "+ 473 tlsString+" on variant "+variant, func() { 474 modelData, err := modelutil.SetupAPITestData(testConfig, "normal_task", variant, filepath.Join(testDirectory, 475 "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 476 testutil.HandleTestingErr(err, t, "Couldn't create test data: %v", err) 477 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 478 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 479 defer testServer.Close() 480 testAgent, err := createAgent(testServer, modelData.Host) 481 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 482 483 assignAgentTask(testAgent, modelData.Task) 484 485 // actually run the task. 486 // this function won't return until the whole thing is done. 487 _, err = testAgent.RunTask() 488 So(err, ShouldBeNil) 489 time.Sleep(100 * time.Millisecond) 490 testAgent.APILogger.FlushAndWait() 491 492 Convey("all scripts in task should have been run successfully", func() { 493 So(scanLogsForTask(modelData.Task.Id, "", "executing the pre-run script"), ShouldBeTrue) 494 So(scanLogsForTask(modelData.Task.Id, "", "executing the post-run script!"), ShouldBeTrue) 495 496 So(scanLogsForTask(modelData.Task.Id, "", "starting normal_task!"), ShouldBeTrue) 497 So(scanLogsForTask(modelData.Task.Id, "", "done with normal_task!"), ShouldBeTrue) 498 So(scanLogsForTask(modelData.Task.Id, model.SystemLogPrefix, "this output should go to the system logs."), ShouldBeTrue) 499 500 var testTask *task.Task 501 testTask, err = task.FindOne(task.ById(modelData.Task.Id)) 502 testutil.HandleTestingErr(err, t, "Couldn't find test task: %v", err) 503 So(testTask.Status, ShouldEqual, evergreen.TaskSucceeded) 504 505 expectedResults := []task.TestResult{ 506 { 507 Status: "success", 508 TestFile: "t1", 509 URL: "url", 510 ExitCode: 0, 511 StartTime: 0, 512 EndTime: 10, 513 }, 514 } 515 So(testTask.TestResults, ShouldResemble, expectedResults) 516 }) 517 }) 518 }) 519 } 520 } 521 } 522 } 523 524 func TestTaskFailures(t *testing.T) { 525 setupTlsConfigs(t) 526 527 testutil.ConfigureIntegrationTest(t, testConfig, "TestTaskFailures") 528 529 for tlsString, tlsConfig := range tlsConfigs { 530 for _, testSetup := range testSetups { 531 Convey(testSetup.testSpec, t, func() { 532 Convey("With agent running a failing test and live API server over "+tlsString, func() { 533 modelData, err := modelutil.SetupAPITestData(testConfig, "failing_task", 534 "linux-64", filepath.Join(testDirectory, "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 535 testutil.HandleTestingErr(err, t, "Couldn't create test data: %v", err) 536 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 537 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 538 defer testServer.Close() 539 testAgent, err := createAgent(testServer, modelData.Host) 540 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 541 542 assignAgentTask(testAgent, modelData.Task) 543 544 // actually run the task. 545 // this function won't return until the whole thing is done. 546 _, err = testAgent.RunTask() 547 So(err, ShouldBeNil) 548 time.Sleep(100 * time.Millisecond) 549 testAgent.APILogger.FlushAndWait() 550 printLogsForTask(modelData.Task.Id) 551 552 Convey("the pre and post-run scripts should have run", func() { 553 So(scanLogsForTask(modelData.Task.Id, "", "executing the pre-run script"), ShouldBeTrue) 554 So(scanLogsForTask(modelData.Task.Id, "", "executing the post-run script!"), ShouldBeTrue) 555 556 Convey("the task should have run up until its first failure", func() { 557 So(scanLogsForTask(modelData.Task.Id, "", "starting failing_task!"), ShouldBeTrue) 558 So(scanLogsForTask(modelData.Task.Id, "", "done with failing_task!"), ShouldBeFalse) 559 }) 560 561 Convey("the tasks's final status should be FAILED", func() { 562 testTask, err := task.FindOne(task.ById(modelData.Task.Id)) 563 testutil.HandleTestingErr(err, t, "Failed to find test task") 564 So(testTask.Status, ShouldEqual, evergreen.TaskFailed) 565 So(testTask.Details.Status, ShouldEqual, evergreen.TaskFailed) 566 So(testTask.Details.Description, ShouldEqual, "failing shell command") 567 So(testTask.Details.TimedOut, ShouldBeFalse) 568 So(testTask.Details.Type, ShouldEqual, model.SystemCommandType) 569 }) 570 }) 571 }) 572 }) 573 } 574 } 575 } 576 577 func TestTaskAbortion(t *testing.T) { 578 setupTlsConfigs(t) 579 580 testutil.ConfigureIntegrationTest(t, testConfig, "TestTaskAbortion") 581 for tlsString, tlsConfig := range tlsConfigs { 582 for _, testSetup := range testSetups { 583 Convey(testSetup.testSpec, t, func() { 584 Convey("With agent running a slow test and live API server over "+tlsString, func() { 585 modelData, err := modelutil.SetupAPITestData(testConfig, "very_slow_task", "linux-64", filepath.Join(testDirectory, 586 "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 587 testutil.HandleTestingErr(err, t, "Failed to find test task") 588 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 589 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 590 defer testServer.Close() 591 testAgent, err := createAgent(testServer, modelData.Host) 592 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 593 594 assignAgentTask(testAgent, modelData.Task) 595 Convey("when the abort signal is triggered on the task", func() { 596 go func() { 597 // Wait for a few seconds, then switch the task to aborted! 598 time.Sleep(2 * time.Second) 599 err := model.AbortTask(modelData.Task.Id, "") 600 testutil.HandleTestingErr(err, t, "Failed to abort test task") 601 fmt.Println("aborted task.") 602 }() 603 604 // actually run the task. 605 // this function won't return until the whole thing is done. 606 _, err := testAgent.RunTask() 607 So(err, ShouldBeNil) 608 609 testAgent.APILogger.Flush() 610 time.Sleep(1 * time.Second) 611 printLogsForTask(modelData.Task.Id) 612 613 Convey("the pre and post-run scripts should have run", func() { 614 So(scanLogsForTask(modelData.Task.Id, "", "executing the pre-run script"), ShouldBeTrue) 615 So(scanLogsForTask(modelData.Task.Id, "", "executing the post-run script!"), ShouldBeTrue) 616 So(scanLogsForTask(modelData.Task.Id, "", "Received abort signal - stopping."), ShouldBeTrue) 617 So(scanLogsForTask(modelData.Task.Id, "", "done with very_slow_task!"), ShouldBeFalse) 618 testTask, err := task.FindOne(task.ById(modelData.Task.Id)) 619 testutil.HandleTestingErr(err, t, "Failed to find test task") 620 So(testTask.Status, ShouldEqual, evergreen.TaskUndispatched) 621 }) 622 }) 623 }) 624 }) 625 } 626 } 627 } 628 629 func TestTaskTimeout(t *testing.T) { 630 setupTlsConfigs(t) 631 for tlsString, tlsConfig := range tlsConfigs { 632 Convey("With agent running a slow test and live API server over "+tlsString, t, func() { 633 modelData, err := modelutil.SetupAPITestData(testConfig, "timeout_task", "linux-64", 634 filepath.Join(testDirectory, "testdata/config_test_plugin/project/evergreen-ci-render.yml"), 635 modelutil.NoPatch) 636 testutil.HandleTestingErr(err, t, "Failed to find test task") 637 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 638 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 639 defer testServer.Close() 640 testAgent, err := createAgent(testServer, modelData.Host) 641 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 642 643 assignAgentTask(testAgent, modelData.Task) 644 Convey("after the slow test runs beyond the timeout threshold", func() { 645 // actually run the task. 646 // this function won't return until the whole thing is done. 647 _, err = testAgent.RunTask() 648 So(err, ShouldBeNil) 649 testAgent.APILogger.Flush() 650 time.Sleep(5 * time.Second) 651 Convey("the test should be marked as failed and timed out", func() { 652 So(scanLogsForTask(modelData.Task.Id, "", "executing the pre-run script"), ShouldBeTrue) 653 So(scanLogsForTask(modelData.Task.Id, "", "executing the post-run script!"), ShouldBeTrue) 654 So(scanLogsForTask(modelData.Task.Id, "", "executing the task-timeout script!"), ShouldBeTrue) 655 testTask, err := task.FindOne(task.ById(modelData.Task.Id)) 656 So(err, ShouldBeNil) 657 So(testTask.Status, ShouldEqual, evergreen.TaskFailed) 658 So(testTask.Details.TimedOut, ShouldBeTrue) 659 So(testTask.Details.Description, ShouldEqual, "shell.exec") 660 }) 661 }) 662 }) 663 } 664 } 665 666 func TestFunctionVariantExclusion(t *testing.T) { 667 setupTlsConfigs(t) 668 for tlsString, tlsConfig := range tlsConfigs { 669 // test against the windows8 and linux-64 variants; linux-64 excludes a test command 670 for _, variant := range []string{"windows8", "linux-64"} { 671 Convey("With agent running a "+variant+" task and live API server over "+tlsString, t, func() { 672 modelData, err := modelutil.SetupAPITestData(testConfig, "variant_test", variant, 673 filepath.Join(testDirectory, "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 674 testutil.HandleTestingErr(err, t, "Failed to find test task") 675 676 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 677 testutil.HandleTestingErr(err, t, "Couldn't create apiserver") 678 defer testServer.Close() 679 testAgent, err := createAgent(testServer, modelData.Host) 680 testutil.HandleTestingErr(err, t, "failed to create agent") 681 682 assignAgentTask(testAgent, modelData.Task) 683 So(modelData.Host.RunningTask, ShouldEqual, modelData.Task.Id) 684 Convey("running the task", func() { 685 686 _, err := testAgent.RunTask() 687 So(err, ShouldBeNil) 688 if variant == "windows8" { 689 Convey("the variant-specific function command should run", func() { 690 So(scanLogsForTask(modelData.Task.Id, "", "variant not excluded!"), ShouldBeTrue) 691 }) 692 } else { 693 Convey("the variant-specific function command should not run", func() { 694 So(scanLogsForTask(modelData.Task.Id, "", "variant not excluded!"), ShouldBeFalse) 695 So(scanLogsForTask(modelData.Task.Id, "", "Skipping command 'shell.exec'"), ShouldBeTrue) 696 }) 697 } 698 }) 699 }) 700 } 701 } 702 } 703 704 func TestTaskCallbackTimeout(t *testing.T) { 705 setupTlsConfigs(t) 706 for tlsString, tlsConfig := range tlsConfigs { 707 Convey("With an agent with callback_timeout_secs=1 and a live API server over "+tlsString, t, func() { 708 modelData, err := modelutil.SetupAPITestData(testConfig, "timeout_task", "linux-64", 709 filepath.Join(testDirectory, "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 710 testutil.HandleTestingErr(err, t, "Failed to find test task") 711 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 712 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 713 defer testServer.Close() 714 testAgent, err := createAgent(testServer, modelData.Host) 715 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 716 717 assignAgentTask(testAgent, modelData.Task) 718 prependConfigToVersion(t, modelData.Task.Version, "callback_timeout_secs: 1\n") 719 720 Convey("after the slow test runs beyond the timeout threshold", func() { 721 // actually run the task. 722 // this function won't return until the whole thing is done. 723 _, err = testAgent.RunTask() 724 So(err, ShouldBeNil) 725 testAgent.APILogger.Flush() 726 time.Sleep(7 * time.Second) 727 So(testAgent.taskConfig.Project.CallbackTimeout, ShouldEqual, 1) 728 Convey("the test should be marked as failed and timed out", func() { 729 So(scanLogsForTask(modelData.Task.Id, "", "executing the pre-run script"), ShouldBeTrue) 730 So(scanLogsForTask(modelData.Task.Id, "", "executing the post-run script!"), ShouldBeTrue) 731 So(scanLogsForTask(modelData.Task.Id, "", "executing the task-timeout script!"), ShouldBeTrue) 732 testTask, err := task.FindOne(task.ById(modelData.Task.Id)) 733 So(err, ShouldBeNil) 734 So(testTask.Status, ShouldEqual, evergreen.TaskFailed) 735 So(testTask.Details.TimedOut, ShouldBeTrue) 736 So(testTask.Details.Description, ShouldEqual, "shell.exec") 737 738 Convey("and the timeout task should have failed", func() { 739 So(scanLogsForTask(modelData.Task.Id, "", "running task-timeout commands in 1"), ShouldBeTrue) 740 }) 741 }) 742 }) 743 }) 744 } 745 } 746 747 func TestTaskExecTimeout(t *testing.T) { 748 setupTlsConfigs(t) 749 for tlsString, tlsConfig := range tlsConfigs { 750 Convey("With agent running a slow test and live API server over "+tlsString, t, func() { 751 modelData, err := modelutil.SetupAPITestData(testConfig, "exec_timeout_task", "linux-64", filepath.Join(testDirectory, "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 752 testutil.HandleTestingErr(err, t, "Failed to find test task") 753 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 754 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 755 defer testServer.Close() 756 testAgent, err := createAgent(testServer, modelData.Host) 757 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 758 759 assignAgentTask(testAgent, modelData.Task) 760 Convey("after the slow test runs beyond the timeout threshold", func() { 761 // actually run the task. 762 // this function won't return until the whole thing is done. 763 _, err = testAgent.RunTask() 764 So(err, ShouldBeNil) 765 testAgent.APILogger.Flush() 766 time.Sleep(5 * time.Second) 767 printLogsForTask(modelData.Task.Id) 768 Convey("the test should be marked as failed and timed out", func() { 769 So(scanLogsForTask(modelData.Task.Id, "", "executing the pre-run script"), ShouldBeTrue) 770 So(scanLogsForTask(modelData.Task.Id, "", "executing the post-run script!"), ShouldBeTrue) 771 So(scanLogsForTask(modelData.Task.Id, "", "executing the task-timeout script!"), ShouldBeTrue) 772 testTask, err := task.FindOne(task.ById(modelData.Task.Id)) 773 So(err, ShouldBeNil) 774 So(testTask.Status, ShouldEqual, evergreen.TaskFailed) 775 So(testTask.Details.TimedOut, ShouldBeTrue) 776 So(testTask.Details.Description, ShouldEqual, "shell.exec") 777 }) 778 }) 779 }) 780 } 781 } 782 783 func TestProjectTaskExecTimeout(t *testing.T) { 784 setupTlsConfigs(t) 785 for tlsString, tlsConfig := range tlsConfigs { 786 Convey("With agent running a slow test and live API server over "+tlsString, t, func() { 787 modelData, err := modelutil.SetupAPITestData(testConfig, "project_exec_timeout_task", "linux-64", filepath.Join(testDirectory, "testdata/config_test_plugin/project/project-timeout-test.yml"), modelutil.NoPatch) 788 testutil.HandleTestingErr(err, t, "Failed to find test task") 789 790 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 791 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 792 defer testServer.Close() 793 794 testAgent, err := createAgent(testServer, modelData.Host) 795 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 796 797 assignAgentTask(testAgent, modelData.Task) 798 799 Convey("after the slow test runs beyond the project timeout threshold", func() { 800 // actually run the task. 801 // this function won't return until the whole thing is done. 802 _, err = testAgent.RunTask() 803 So(err, ShouldBeNil) 804 testAgent.APILogger.Flush() 805 time.Sleep(5 * time.Second) 806 printLogsForTask(modelData.Task.Id) 807 Convey("the test should be marked as failed and timed out", func() { 808 So(scanLogsForTask(modelData.Task.Id, "", "executing the pre-run script"), ShouldBeTrue) 809 So(scanLogsForTask(modelData.Task.Id, "", "executing the post-run script!"), ShouldBeTrue) 810 So(scanLogsForTask(modelData.Task.Id, "", "executing the task-timeout script!"), ShouldBeTrue) 811 testTask, err := task.FindOne(task.ById(modelData.Task.Id)) 812 So(err, ShouldBeNil) 813 So(testTask.Status, ShouldEqual, evergreen.TaskFailed) 814 So(testTask.Details.TimedOut, ShouldBeTrue) 815 So(testTask.Details.Description, ShouldEqual, "shell.exec") 816 }) 817 }) 818 }) 819 } 820 } 821 822 func TestTaskEndEndpoint(t *testing.T) { 823 setupTlsConfigs(t) 824 for tlsString, tlsConfig := range tlsConfigs { 825 modelData, err := modelutil.SetupAPITestData(testConfig, "random", "linux-64", filepath.Join(testDirectory, "testdata/config_test_plugin/project/evergreen-ci-render.yml"), modelutil.NoPatch) 826 testutil.HandleTestingErr(err, t, "Couldn't make test data: %v", err) 827 828 Convey("With a live api server, agent, and test task over "+tlsString, t, func() { 829 testServer, err := service.CreateTestServer(testConfig, tlsConfig, plugin.APIPlugins) 830 testutil.HandleTestingErr(err, t, "Couldn't create apiserver: %v", err) 831 defer testServer.Close() 832 833 testAgent, err := createAgent(testServer, modelData.Host) 834 testutil.HandleTestingErr(err, t, "failed to create agent: %v") 835 assignAgentTask(testAgent, modelData.Task) 836 837 testAgent.heartbeater.Interval = 10 * time.Second 838 testAgent.StartBackgroundActions(&NoopSignalHandler{}) 839 840 Convey("calling end() should update task's/host's status properly ", func() { 841 details := &apimodels.TaskEndDetail{Status: evergreen.TaskSucceeded} 842 taskEndResp, err := testAgent.End(details) 843 time.Sleep(1 * time.Second) 844 So(err, ShouldBeNil) 845 So(taskEndResp.ShouldExit, ShouldBeFalse) 846 847 taskUpdate, err := task.FindOne(task.ById(modelData.Task.Id)) 848 So(err, ShouldBeNil) 849 So(taskUpdate.Status, ShouldEqual, evergreen.TaskSucceeded) 850 851 testHost, err := host.FindOne(host.ById(modelData.Task.HostId)) 852 So(err, ShouldBeNil) 853 So(testHost.RunningTask, ShouldEqual, "") 854 855 }) 856 }) 857 } 858 } 859 860 // scanLogsForTask examines log messages stored under the given taskId and returns true 861 // if the string scanFor appears in their contents at least once. If a non-empty value is supplied 862 // for logTypes, only logs within the specified log types will 863 // be scanned for the given string (see SystemLogPrefix, AgentLogPrefix, TaskLogPrefix). 864 func scanLogsForTask(taskId string, logTypes string, scanFor string) bool { 865 taskLogs, err := model.FindAllTaskLogs(taskId, 0) 866 if err != nil { 867 panic(err) 868 } 869 for _, taskLogObj := range taskLogs { 870 for _, logmsg := range taskLogObj.Messages { 871 if logTypes != "" && !strings.Contains(logTypes, logmsg.Type) { 872 continue 873 } 874 if strings.Contains(strings.ToLower(logmsg.Message), strings.ToLower(scanFor)) { 875 return true 876 } 877 } 878 } 879 return false 880 } 881 882 func printLogsForTask(taskId string) { 883 logMessages, err := model.FindMostRecentLogMessages(taskId, 0, 100, []string{}, []string{}) 884 if err != nil { 885 panic(err) 886 } 887 for i := len(logMessages) - 1; i >= 0; i-- { 888 if logMessages[i].Type == model.SystemLogPrefix { 889 continue 890 } 891 fmt.Println(logMessages[i].Message) 892 } 893 } 894 895 type patchRequest struct { 896 moduleName string 897 filePath string 898 githash string 899 } 900 901 func setupPatches(patchMode modelutil.PatchTestMode, b *build.Build, t *testing.T, patches ...patchRequest) { 902 if patchMode == modelutil.NoPatch { 903 return 904 } 905 906 ptch := &patch.Patch{ 907 Status: evergreen.PatchCreated, 908 Version: b.Version, 909 Patches: []patch.ModulePatch{}, 910 } 911 912 for _, p := range patches { 913 patchContent, err := ioutil.ReadFile(p.filePath) 914 testutil.HandleTestingErr(err, t, "failed to read test patch file") 915 916 if patchMode == modelutil.InlinePatch { 917 ptch.Patches = append(ptch.Patches, patch.ModulePatch{ 918 ModuleName: p.moduleName, 919 Githash: p.githash, 920 PatchSet: patch.PatchSet{Patch: string(patchContent)}, 921 }) 922 } else { 923 pId := bson.NewObjectId().Hex() 924 So(dbutil.WriteGridFile(patch.GridFSPrefix, pId, strings.NewReader(string(patchContent))), ShouldBeNil) 925 ptch.Patches = append(ptch.Patches, patch.ModulePatch{ 926 ModuleName: p.moduleName, 927 Githash: p.githash, 928 PatchSet: patch.PatchSet{PatchFileId: pId}, 929 }) 930 } 931 } 932 testutil.HandleTestingErr(ptch.Insert(), t, "failed to insert patch") 933 } 934 935 // prependConfigToVersion modifies the project config with the given id 936 func prependConfigToVersion(t *testing.T, versionId, configData string) { 937 v, err := version.FindOne(version.ById(versionId)) 938 testutil.HandleTestingErr(err, t, "failed to load version") 939 if v == nil { 940 err = errors.New("could not find version to update") 941 testutil.HandleTestingErr(err, t, "failed to find version") 942 } 943 v.Config = configData + v.Config 944 testutil.HandleTestingErr(dbutil.ClearCollections(version.Collection), t, "couldnt reset version") 945 testutil.HandleTestingErr(v.Insert(), t, "failed to insert version") 946 }