Ejemplo n.º 1
0
func TestNotify(t *testing.T) {
	if evergreen.TestConfig().Notify.LogFile != "" {
		evergreen.SetLogger(evergreen.TestConfig().Notify.LogFile)
	}
	db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(TestConfig))
	emailSubjects = make([]string, 0)
	emailBodies = make([]string, 0)

	Convey("When running notification handlers", t, func() {

		ae, err := createEnvironment(TestConfig, map[string]interface{}{})
		So(err, ShouldBeNil)

		Convey("Build-specific handlers should return the correct emails", func() {
			cleanupdb()
			timeNow := time.Now()
			// insert the test documents
			insertBuildDocs(timeNow)
			version := &version.Version{Id: "version"}
			So(version.Insert(), ShouldBeNil)
			Convey("BuildFailureHandler should return 1 email per failed build", func() {
				handler := BuildFailureHandler{}
				emails, err := handler.GetNotifications(ae, "config_test",
					&buildFailureNotificationKey)
				So(err, ShouldBeNil)
				// check that we only returned 2 failed notifications
				So(len(emails), ShouldEqual, 2)
				So(emails[0].GetSubject(), ShouldEqual,
					"[MCI-FAILURE ] Build #build1 failed on displayName")
				So(emails[1].GetSubject(), ShouldEqual,
					"[MCI-FAILURE ] Build #build9 failed on displayName")
			})

			Convey("BuildSuccessHandler should return 1 email per successful build", func() {
				handler := BuildSuccessHandler{}
				emails, err := handler.GetNotifications(ae, "config_test",
					&buildSucceessNotificationKey)
				So(err, ShouldBeNil)
				// check that we only returned 2 success notifications
				So(len(emails), ShouldEqual, 2)
				So(emails[0].GetSubject(), ShouldEqual,
					"[MCI-SUCCESS ] Build #build3 succeeded on displayName")
				So(emails[1].GetSubject(), ShouldEqual,
					"[MCI-SUCCESS ] Build #build8 succeeded on displayName")
			})

			Convey("BuildCompletionHandler should return 1 email per completed build", func() {
				handler := BuildCompletionHandler{}
				emails, err := handler.GetNotifications(ae, "config_test",
					&buildCompletionNotificationKey)
				So(err, ShouldBeNil)
				// check that we only returned 6 completed notifications
				So(len(emails), ShouldEqual, 4)
				So(emails[0].GetSubject(), ShouldEqual,
					"[MCI-COMPLETION ] Build #build1 completed on displayName")
				So(emails[1].GetSubject(), ShouldEqual,
					"[MCI-COMPLETION ] Build #build3 completed on displayName")
				So(emails[2].GetSubject(), ShouldEqual,
					"[MCI-COMPLETION ] Build #build8 completed on displayName")
				So(emails[3].GetSubject(), ShouldEqual,
					"[MCI-COMPLETION ] Build #build9 completed on displayName")
			})

			Convey("BuildSuccessToFailureHandler should return 1 email per "+
				"build success to failure transition", func() {
				handler := BuildSuccessToFailureHandler{}
				emails, err := handler.GetNotifications(ae, "config_test",
					&buildSuccessToFailureNotificationKey)
				So(err, ShouldBeNil)
				// check that we only returned 1 success_to_failure notifications
				So(len(emails), ShouldEqual, 1)
				So(emails[0].GetSubject(), ShouldEqual,
					"[MCI-FAILURE ] Build #build9 transitioned to failure on displayName")
			})
		})

		Convey("Task-specific handlers should return the correct emails", func() {
			cleanupdb()
			timeNow := time.Now()
			// insert the test documents
			insertTaskDocs(timeNow)
			v := &version.Version{Id: "version"}
			So(v.Insert(), ShouldBeNil)

			Convey("TaskFailureHandler should return 1 email per task failure", func() {
				handler := TaskFailureHandler{}
				emails, err := handler.GetNotifications(ae, "config_test",
					&taskFailureNotificationKey)
				So(err, ShouldBeNil)
				// check that we only returned 2 failed notifications
				So(len(emails), ShouldEqual, 2)
				So(emails[0].GetSubject(), ShouldEqual,
					"[MCI-FAILURE ] possible MCI failure in displayName (failed on build1)")
				So(emails[1].GetSubject(), ShouldEqual,
					"[MCI-FAILURE ] possible MCI failure in displayName (failed on build1)")
			})

			Convey("TaskSuccessHandler should return 1 email per task success", func() {
				handler := TaskSuccessHandler{}
				emails, err := handler.GetNotifications(ae, "config_test",
					&taskSucceessNotificationKey)
				So(err, ShouldBeNil)
				// check that we only returned 2 success notifications
				So(len(emails), ShouldEqual, 2)
				So(emails[0].GetSubject(), ShouldEqual,
					"[MCI-SUCCESS ] possible MCI failure in displayName (succeeded on build1)")
				So(emails[1].GetSubject(), ShouldEqual,
					"[MCI-SUCCESS ] possible MCI failure in displayName (succeeded on build1)")
			})

			Convey("TaskCompletionHandler should return 1 email per completed task", func() {
				handler := TaskCompletionHandler{}
				emails, err := handler.GetNotifications(ae, "config_test",
					&taskCompletionNotificationKey)
				So(err, ShouldBeNil)
				// check that we only returned 6 completion notifications
				So(len(emails), ShouldEqual, 4)
				So(emails[0].GetSubject(), ShouldEqual,
					"[MCI-COMPLETION ] possible MCI failure in displayName (completed on build1)")
				So(emails[1].GetSubject(), ShouldEqual,
					"[MCI-COMPLETION ] possible MCI failure in displayName (completed on build1)")
				So(emails[2].GetSubject(), ShouldEqual,
					"[MCI-COMPLETION ] possible MCI failure in displayName (completed on build1)")
				So(emails[3].GetSubject(), ShouldEqual,
					"[MCI-COMPLETION ] possible MCI failure in displayName (completed on build1)")
			})

			Convey("TaskSuccessToFailureHandler should return 1 email per "+
				"task success to failure transition", func() {
				handler := TaskSuccessToFailureHandler{}
				emails, err := handler.GetNotifications(ae, "config_test",
					&taskSuccessToFailureNotificationKey)
				So(err, ShouldBeNil)
				// check that we only returned 1 success to failure notifications
				So(len(emails), ShouldEqual, 1)
				So(emails[0].GetSubject(), ShouldEqual,
					"[MCI-FAILURE ] possible MCI failure in displayName (transitioned to "+
						"failure on build1)")
			})
		})
	})

	Convey("When running notifications pipeline", t, func() {
		cleanupdb()
		timeNow := time.Now()
		// insert the test documents
		insertTaskDocs(timeNow)
		v := &version.Version{Id: "version"}
		So(v.Insert(), ShouldBeNil)

		Convey("Should run the correct notification handlers for given "+
			"notification keys", func() {
			notificationSettings := &MCINotification{}
			notificationSettings.Notifications = []Notification{
				Notification{"task_failure", "project", []string{"user@mongodb"}, []string{}},
				Notification{"task_success_to_failure", "project", []string{"user@mongodb"}, []string{}},
			}
			notificationSettings.Teams = []Team{
				Team{
					"myteam",
					"*****@*****.**",
					[]Subscription{Subscription{"task", []string{}, []string{"task_failure"}}},
				},
			}
			notificationSettings.PatchNotifications = []Subscription{
				Subscription{"patch_project", []string{}, []string{}},
			}

			notificationKeyFailure := NotificationKey{"project", "task_failure", "task", "gitter_request"}
			notificationKeyToFailure := NotificationKey{"project", "task_success_to_failure", "task",
				"gitter_request"}

			ae, err := createEnvironment(TestConfig, map[string]interface{}{})
			So(err, ShouldBeNil)

			emails, err := ProcessNotifications(ae, "config_test", notificationSettings, false)
			So(err, ShouldBeNil)

			So(len(emails[notificationKeyFailure]), ShouldEqual, 2)
			So(emails[notificationKeyFailure][0].GetSubject(), ShouldEqual,
				"[MCI-FAILURE ] possible MCI failure in displayName (failed on build1)")
			So(emails[notificationKeyFailure][1].GetSubject(), ShouldEqual,
				"[MCI-FAILURE ] possible MCI failure in displayName (failed on build1)")

			So(len(emails[notificationKeyToFailure]), ShouldEqual, 1)
			So(emails[notificationKeyToFailure][0].GetSubject(), ShouldEqual,
				"[MCI-FAILURE ] possible MCI failure in displayName (transitioned to "+
					"failure on build1)")
		})

		Convey("SendNotifications should send emails correctly", func() {
			notificationSettings := &MCINotification{}
			notificationSettings.Notifications = []Notification{
				Notification{"task_failure", "project", []string{"user@mongodb"}, []string{}},
			}
			notificationSettings.Teams = []Team{
				Team{
					"myteam",
					"*****@*****.**",
					[]Subscription{Subscription{"task", []string{}, []string{"task_failure"}}},
				},
			}
			notificationSettings.PatchNotifications = []Subscription{
				Subscription{"patch_project", []string{}, []string{}},
			}

			fakeTask, err := model.FindOneTask(bson.M{"_id": "task8"}, bson.M{}, []string{})

			notificationKey := NotificationKey{"project", "task_failure", "task", "gitter_request"}

			triggeredNotification := TriggeredTaskNotification{
				fakeTask,
				nil,
				[]ChangeInfo{},
				notificationKey,
				"[MCI-FAILURE]",
				"failed",
			}

			email := TaskEmail{
				EmailBase{
					"This is the email body",
					"This is the email subject",
					triggeredNotification.Info,
				},
				triggeredNotification,
			}

			m := make(map[NotificationKey][]Email)
			m[notificationKey] = []Email{&email}

			mailer := MockMailer{}
			mockSettings := evergreen.Settings{Notify: evergreen.NotifyConfig{}}
			err = SendNotifications(&mockSettings, notificationSettings, m, mailer)
			So(err, ShouldBeNil)

			So(len(emailSubjects), ShouldEqual, 1)
			So(emailSubjects[0], ShouldEqual,
				"This is the email subject")
			So(emailBodies[0], ShouldEqual,
				"This is the email body")
		})
	})
}
Ejemplo n.º 2
0
func (uis *UIServer) taskPage(w http.ResponseWriter, r *http.Request) {
	projCtx := MustHaveProjectContext(r)

	if projCtx.Task == nil {
		http.Error(w, "Not found", http.StatusNotFound)
		return
	}

	if projCtx.Build == nil {
		uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("build not found"))
		return
	}

	if projCtx.Version == nil {
		uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("version not found"))
		return
	}

	if projCtx.ProjectRef == nil {
		evergreen.Logger.Logf(slogger.ERROR, "Project ref is nil")
		uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("version not found"))
		return
	}

	executionStr := mux.Vars(r)["execution"]
	archived := false
	if executionStr != "" {
		// otherwise we can look in either tasks or old_tasks
		// where tasks are looked up in the old_tasks collection with key made up of
		// the original key and the execution number joined by an "_"
		// and the tasks are looked up in the tasks collection by key and execution
		// number, so that we avoid finding the wrong execution in the tasks
		// collection
		execution, err := strconv.Atoi(executionStr)
		if err != nil {
			http.Error(w, fmt.Sprintf("Bad execution number: %v", executionStr), http.StatusBadRequest)
			return
		}
		oldTaskId := fmt.Sprintf("%v_%v", projCtx.Task.Id, executionStr)
		taskFromDb, err := model.FindOneOldTask(bson.M{"_id": oldTaskId}, db.NoProjection, db.NoSort)
		if err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
		archived = true
		if taskFromDb == nil {
			// for backwards compatibility with tasks without an execution
			if execution == 0 {
				taskFromDb, err = model.FindOneTask(bson.M{
					"$and": []bson.M{
						bson.M{"_id": projCtx.Task.Id},
						bson.M{"$or": []bson.M{bson.M{"execution": 0}, bson.M{"execution": nil}}}}},
					db.NoProjection,
					db.NoSort)
			} else {
				taskFromDb, err = model.FindOneTask(bson.M{"_id": projCtx.Task.Id, "execution": execution},
					db.NoProjection, db.NoSort)
			}
			if err != nil {
				uis.LoggedError(w, r, http.StatusInternalServerError, fmt.Errorf("Error finding old task: %v", err))
				return
			}
		}
		projCtx.Task = taskFromDb
	}

	// Build a struct containing the subset of task data needed for display in the UI

	task := uiTaskData{
		Id:                  projCtx.Task.Id,
		DisplayName:         projCtx.Task.DisplayName,
		Revision:            projCtx.Task.Revision,
		Status:              projCtx.Task.Status,
		TaskEndDetails:      projCtx.Task.Details,
		Distro:              projCtx.Task.DistroId,
		BuildVariant:        projCtx.Task.BuildVariant,
		BuildId:             projCtx.Task.BuildId,
		Activated:           projCtx.Task.Activated,
		Restarts:            projCtx.Task.Restarts,
		Execution:           projCtx.Task.Execution,
		Requester:           projCtx.Task.Requester,
		StartTime:           projCtx.Task.StartTime.UnixNano(),
		DispatchTime:        projCtx.Task.DispatchTime.UnixNano(),
		FinishTime:          projCtx.Task.FinishTime.UnixNano(),
		ExpectedDuration:    projCtx.Task.ExpectedDuration,
		PushTime:            projCtx.Task.PushTime,
		TimeTaken:           projCtx.Task.TimeTaken,
		Priority:            projCtx.Task.Priority,
		TestResults:         projCtx.Task.TestResults,
		Aborted:             projCtx.Task.Aborted,
		CurrentTime:         time.Now().UnixNano(),
		BuildVariantDisplay: projCtx.Build.DisplayName,
		Message:             projCtx.Version.Message,
		Project:             projCtx.Version.Identifier,
		Author:              projCtx.Version.Author,
		AuthorEmail:         projCtx.Version.AuthorEmail,
		VersionId:           projCtx.Version.Id,
		RepoOwner:           projCtx.ProjectRef.Owner,
		Repo:                projCtx.ProjectRef.Repo,
		Archived:            archived,
	}

	deps, taskWaiting, err := getTaskDependencies(projCtx.Task)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	task.DependsOn = deps
	task.TaskWaiting = taskWaiting

	// Activating and deactivating tasks should clear out the
	// MinQueuePos but just in case, lets not show it if we shouldn't
	if projCtx.Task.Status == evergreen.TaskUndispatched && projCtx.Task.Activated {
		task.MinQueuePos = projCtx.Task.MinQueuePos
	}

	if projCtx.Task.HostId != "" {
		task.HostDNS = projCtx.Task.HostId
		task.HostId = projCtx.Task.HostId
		taskHost, err := host.FindOne(host.ById(projCtx.Task.HostId))
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		if taskHost != nil {
			task.HostDNS = taskHost.Host
		}
	}

	if projCtx.Patch != nil {
		taskOnBaseCommit, err := projCtx.Task.FindTaskOnBaseCommit()
		if err != nil {
			uis.LoggedError(w, r, http.StatusInternalServerError, err)
			return
		}
		taskPatch := &uiPatch{Patch: *projCtx.Patch}
		if taskOnBaseCommit != nil {
			taskPatch.BaseTaskId = taskOnBaseCommit.Id
		}
		taskPatch.StatusDiffs = model.StatusDiffTasks(taskOnBaseCommit, projCtx.Task).Tests
		task.PatchInfo = taskPatch
	}

	flashes := PopFlashes(uis.CookieStore, r, w)

	pluginContext := projCtx.ToPluginContext(uis.Settings, GetUser(r))
	pluginContent := getPluginDataAndHTML(uis, plugin.TaskPage, pluginContext)

	uis.WriteHTML(w, http.StatusOK, struct {
		ProjectData   projectContext
		User          *user.DBUser
		Flashes       []interface{}
		Task          uiTaskData
		PluginContent pluginData
	}{projCtx, GetUser(r), flashes, task, pluginContent}, "base",
		"task.html", "base_angular.html", "menu.html")
}