func getTaskTriggerContext(task *model.Task) (*triggerContext, error) { ctx := triggerContext{task: task} tasks, err := model.FindTasks(lastFinishedQ(task.Project, task.DisplayName, task.BuildVariant, task.RevisionOrderNumber)) if err != nil { return nil, err } if len(tasks) > 0 { ctx.previousCompleted = &tasks[0] } return &ctx, nil }
func TestCLIFunctions(t *testing.T) { testutil.ConfigureIntegrationTest(t, testConfig, "TestCLIFunctions") Convey("with API test server running", t, func() { // create a test API server testServer, err := apiserver.CreateTestServer(testConfig, nil, plugin.APIPlugins, true) // create a test user So(db.Clear(user.Collection), ShouldBeNil) So(db.Clear(patch.Collection), ShouldBeNil) So(db.Clear(model.ProjectRefCollection), ShouldBeNil) So((&user.DBUser{Id: "testuser", APIKey: "testapikey", EmailAddress: "*****@*****.**"}).Insert(), ShouldBeNil) localConfBytes, err := ioutil.ReadFile("testdata/sample.yml") So(err, ShouldBeNil) projectRef := &model.ProjectRef{ Identifier: "sample", Owner: "evergreen-ci", Repo: "sample", RepoKind: "github", Branch: "master", RemotePath: "evergreen.yml", LocalConfig: string(localConfBytes), Enabled: true, BatchTime: 180, } So(projectRef.Insert(), ShouldBeNil) // create a settings file for the command line client settings := Settings{ APIServerHost: testServer.URL + "/api", UIServerHost: "http://dev-evg.mongodb.com", APIKey: "testapikey", User: "******", } settingsFile, err := ioutil.TempFile("", "settings") So(err, ShouldBeNil) settingsBytes, err := yaml.Marshal(settings) So(err, ShouldBeNil) _, err = settingsFile.Write(settingsBytes) So(err, ShouldBeNil) settingsFile.Close() t.Log("Wrote settings file to ", settingsFile.Name()) ac, _, err := getAPIClient(&Options{settingsFile.Name()}) So(err, ShouldBeNil) Convey("check that creating a patch works", func() { Convey("user should start with no patches present", func() { patches, err := ac.GetPatches() So(err, ShouldBeNil) So(len(patches), ShouldEqual, 0) }) Convey("Creating a simple patch should be successful", func() { patchSub := patchSubmission{"sample", testPatch, "sample patch", "3c7bfeb82d492dc453e7431be664539c35b5db4b", "all", []string{"all"}, false} newPatch, err := ac.PutPatch(patchSub) So(err, ShouldBeNil) Convey("Newly created patch should be fetchable via API", func() { patches, err := ac.GetPatches() So(err, ShouldBeNil) So(len(patches), ShouldEqual, 1) }) Convey("Adding a module to the patch should work", func() { err = ac.UpdatePatchModule(newPatch.Id.Hex(), "render-module", testPatch, "1e5232709595db427893826ce19289461cba3f75") So(err, ShouldBeNil) patches, err := ac.GetPatches() So(err, ShouldBeNil) So(patches[0].Patches[0].ModuleName, ShouldEqual, "") So(patches[0].Patches[1].ModuleName, ShouldEqual, "render-module") Convey("Removing the module from the patch should work", func() { So(ac.DeletePatchModule(newPatch.Id.Hex(), "render-module"), ShouldBeNil) patches, err := ac.GetPatches() So(err, ShouldBeNil) So(len(patches[0].Patches), ShouldEqual, 1) Convey("Finalizing the patch should work", func() { // First double check that the patch starts with no "version" field So(patches[0].Version, ShouldEqual, "") So(ac.FinalizePatch(newPatch.Id.Hex()), ShouldBeNil) patches, err := ac.GetPatches() So(err, ShouldBeNil) // After finalizing, the patch should now have a version populated So(patches[0].Version, ShouldNotEqual, "") Convey("Cancelling the patch should work", func() { So(ac.CancelPatch(newPatch.Id.Hex()), ShouldBeNil) patches, err := ac.GetPatches() So(err, ShouldBeNil) // After cancelling, tasks in the version should be deactivated tasks, err := model.FindTasks(db.Query(bson.M{model.TaskVersionKey: patches[0].Version})) So(err, ShouldBeNil) for _, t := range tasks { So(t.Activated, ShouldBeFalse) } }) }) }) }) }) Convey("Creating a complex patch should be successful", func() { patchSub := patchSubmission{"sample", testPatch, "sample patch #2", "3c7bfeb82d492dc453e7431be664539c35b5db4b", "osx-108", []string{"failing_test"}, false} _, err := ac.PutPatch(patchSub) So(err, ShouldBeNil) Convey("Newly created patch should be fetchable via API", func() { patches, err := ac.GetPatches() Println(patches) So(err, ShouldBeNil) So(len(patches), ShouldEqual, 1) So(len(patches[0].BuildVariants), ShouldEqual, 1) So(patches[0].BuildVariants[0], ShouldEqual, "osx-108") So(len(patches[0].Tasks), ShouldEqual, 2) So(patches[0].Tasks, ShouldContain, "failing_test") Convey("and have expanded dependencies", func() { So(patches[0].Tasks, ShouldContain, "compile") }) }) }) }) }) }
// GetRoutes returns an API route for serving patch data. func (jsp *JSONPlugin) GetAPIHandler() http.Handler { r := mux.NewRouter() r.HandleFunc("/tags/{task_name}/{name}", func(w http.ResponseWriter, r *http.Request) { t := plugin.GetTask(r) if t == nil { http.Error(w, "task not found", http.StatusNotFound) return } tagged := []TaskJSON{} jsonQuery := db.Query(bson.M{ "project_id": t.Project, "variant": t.BuildVariant, "task_name": mux.Vars(r)["task_name"], "tag": bson.M{"$exists": true, "$ne": ""}, "name": mux.Vars(r)["name"]}) err := db.FindAllQ(collection, jsonQuery, &tagged) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } plugin.WriteJSON(w, http.StatusOK, tagged) }) r.HandleFunc("/history/{task_name}/{name}", func(w http.ResponseWriter, r *http.Request) { t := plugin.GetTask(r) if t == nil { http.Error(w, "task not found", http.StatusNotFound) return } before := []TaskJSON{} jsonQuery := db.Query(bson.M{ "project_id": t.Project, "variant": t.BuildVariant, "order": bson.M{"$lte": t.RevisionOrderNumber}, "task_name": mux.Vars(r)["task_name"], "is_patch": false, "name": mux.Vars(r)["name"]}).Sort([]string{"-order"}).Limit(100) err := db.FindAllQ(collection, jsonQuery, &before) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } //reverse order of "before" because we had to sort it backwards to apply the limit correctly: for i, j := 0, len(before)-1; i < j; i, j = i+1, j-1 { before[i], before[j] = before[j], before[i] } after := []TaskJSON{} jsonAfterQuery := db.Query(bson.M{ "project_id": t.Project, "variant": t.BuildVariant, "order": bson.M{"$gt": t.RevisionOrderNumber}, "task_name": mux.Vars(r)["task_name"], "is_patch": false, "name": mux.Vars(r)["name"]}).Sort([]string{"order"}).Limit(100) err = db.FindAllQ(collection, jsonAfterQuery, &after) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } //concatenate before + after before = append(before, after...) plugin.WriteJSON(w, http.StatusOK, before) }) r.HandleFunc("/data/{name}", func(w http.ResponseWriter, r *http.Request) { task := plugin.GetTask(r) if task == nil { http.Error(w, "task not found", http.StatusNotFound) return } name := mux.Vars(r)["name"] rawData := map[string]interface{}{} err := util.ReadJSONInto(r.Body, &rawData) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } jsonBlob := TaskJSON{ TaskId: task.Id, TaskName: task.DisplayName, Name: name, BuildId: task.BuildId, Variant: task.BuildVariant, ProjectId: task.Project, VersionId: task.Version, Revision: task.Revision, RevisionOrderNumber: task.RevisionOrderNumber, Data: rawData, IsPatch: task.Requester == evergreen.PatchVersionRequester, } _, err = db.Upsert(collection, bson.M{"task_id": task.Id, "name": name}, jsonBlob) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } plugin.WriteJSON(w, http.StatusOK, "ok") return }) r.HandleFunc("/data/{task_name}/{name}", func(w http.ResponseWriter, r *http.Request) { task := plugin.GetTask(r) if task == nil { http.Error(w, "task not found", http.StatusNotFound) return } name := mux.Vars(r)["name"] taskName := mux.Vars(r)["task_name"] var jsonForTask TaskJSON err := db.FindOneQ(collection, db.Query(bson.M{"version_id": task.Version, "build_id": task.BuildId, "name": name, "task_name": taskName}), &jsonForTask) if err != nil { if err == mgo.ErrNotFound { plugin.WriteJSON(w, http.StatusNotFound, nil) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } plugin.WriteJSON(w, http.StatusOK, jsonForTask.Data) }) r.HandleFunc("/data/{task_name}/{name}/{variant}", func(w http.ResponseWriter, r *http.Request) { task := plugin.GetTask(r) if task == nil { http.Error(w, "task not found", http.StatusNotFound) return } name := mux.Vars(r)["name"] taskName := mux.Vars(r)["task_name"] variantId := mux.Vars(r)["variant"] // Find the task for the other variant, if it exists ts, err := model.FindTasks(db.Query(bson.M{model.TaskVersionKey: task.Version, model.TaskBuildVariantKey: variantId, model.TaskDisplayNameKey: taskName}).Limit(1)) if err != nil { if err == mgo.ErrNotFound { plugin.WriteJSON(w, http.StatusNotFound, nil) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } if len(ts) == 0 { plugin.WriteJSON(w, http.StatusNotFound, nil) return } otherVariantTask := ts[0] var jsonForTask TaskJSON err = db.FindOneQ(collection, db.Query(bson.M{"task_id": otherVariantTask.Id, "name": name}), &jsonForTask) if err != nil { if err == mgo.ErrNotFound { plugin.WriteJSON(w, http.StatusNotFound, nil) return } http.Error(w, err.Error(), http.StatusInternalServerError) return } plugin.WriteJSON(w, http.StatusOK, jsonForTask.Data) }) return r }