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  }