github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/notify/task_handler.go (about) 1 package notify 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/evergreen-ci/evergreen/apimodels" 8 "github.com/evergreen-ci/evergreen/model/task" 9 "github.com/evergreen-ci/evergreen/model/version" 10 "github.com/evergreen-ci/evergreen/web" 11 "github.com/mongodb/grip" 12 "github.com/pkg/errors" 13 ) 14 15 const ( 16 TimeOutMessage = "timeout" 17 UnresponsiveMessage = "unresponsive" 18 ) 19 20 // "Base class" for all task_*_handler.go structs. Contains code that's common 21 // to all the task_*_handlers. Note that this struct does NOT implement 22 // NotificationHandler 23 type TaskNotificationHandler struct { 24 Type string 25 } 26 27 type TaskNotificationForTemplate struct { 28 Notification *TriggeredTaskNotification 29 LogsUrl string 30 Details apimodels.TaskEndDetail 31 FailedTests []task.TestResult 32 Subject string 33 } 34 35 // convenience wrapper about everything we want to know about a task 36 // notification before it goes off for templating. 37 type TriggeredTaskNotification struct { 38 Current *task.Task 39 Previous *task.Task 40 Info []ChangeInfo 41 Key NotificationKey 42 Preface string 43 Transition string 44 } 45 46 func (self *TaskNotificationHandler) getRecentlyFinishedTasksWithStatus(key *NotificationKey, 47 status string, preface string, transition string) ([]TriggeredTaskNotification, error) { 48 taskNotifications := []TriggeredTaskNotification{} 49 tasks, err := getRecentlyFinishedTasks(key) 50 if err != nil { 51 return nil, err 52 } 53 54 for _, currentTask := range tasks { 55 // Copy by value to make pointer safe 56 curr := currentTask 57 if status == "" || curr.Status == status { 58 grip.Debugf("Adding '%s' on %s %s notification", 59 curr.Id, key.Project, key.NotificationName) 60 61 // get the task's project to add to the notification subject line 62 branchName := UnknownProjectBranch 63 if projectRef, err := getProjectRef(curr.Project); err != nil { 64 grip.Warningf("Unable to find project ref for task '%s': %v", 65 curr.Id, err) 66 } else if projectRef != nil { 67 branchName = projectRef.Branch 68 } 69 70 notification := TriggeredTaskNotification{ 71 Current: &curr, 72 Previous: nil, 73 Key: *key, 74 Preface: fmt.Sprintf(preface, branchName), 75 Transition: transition, 76 } 77 taskNotifications = append(taskNotifications, notification) 78 } 79 } 80 return taskNotifications, nil 81 } 82 83 func (self *TaskNotificationHandler) templateNotification(ae *web.App, 84 notification *TriggeredTaskNotification, changeInfo []ChangeInfo) (email Email, err error) { 85 // *This could potential break some buildlogger links when MCI changes version as in-progress 86 // tasks will still be using the previous version number.* 87 if err != nil { 88 grip.Errorln("Error getting MCI version:", err) 89 return 90 } 91 92 current := notification.Current 93 taskNotification := TaskNotificationForTemplate{} 94 taskNotification.Notification = notification 95 96 displayName := getDisplayName(current.BuildId) 97 98 // add the task end status details 99 taskNotification.Details = current.Details 100 101 // add change information to notification 102 notification.Info = changeInfo 103 104 // get the failed tests (if any) 105 taskNotification.FailedTests = getFailedTests(current, notification.Key.NotificationName) 106 var testFailureMessage string 107 switch len(taskNotification.FailedTests) { 108 case 0: 109 if current.Details.TimedOut { 110 testFailureMessage = TimeOutMessage 111 if current.Details.Description == task.AgentHeartbeat { 112 testFailureMessage = UnresponsiveMessage 113 } 114 } else { 115 testFailureMessage = "possible MCI failure" 116 } 117 case 1: 118 testFailureMessage = taskNotification.FailedTests[0].TestFile 119 default: 120 testFailureMessage = fmt.Sprintf("%v tests failed", 121 len(taskNotification.FailedTests)) 122 } 123 124 // construct the task notification subject line 125 subject := fmt.Sprintf("%v %v in %v (%v on %v)", notification.Preface, 126 testFailureMessage, current.DisplayName, notification.Transition, displayName) 127 taskNotification.Subject = subject 128 129 taskNotification.LogsUrl = fmt.Sprintf("task/%v", current.Id) 130 131 // template task notification body 132 body, err := TemplateEmailBody(ae, "task_notification.html", taskNotification) 133 if err != nil { 134 return 135 } 136 137 email = &TaskEmail{EmailBase{body, subject, notification.Info}, *notification} 138 return 139 } 140 141 func (self *TaskNotificationHandler) constructChangeInfo(allTasks []task.Task, 142 key *NotificationKey) ([]ChangeInfo, error) { 143 changeInfoSlice := make([]ChangeInfo, 0) 144 145 for _, task := range allTasks { 146 // add blamelist information for each task 147 v, err := version.FindOne(version.ById(task.Version)) 148 if err != nil { 149 return changeInfoSlice, err 150 } 151 if v == nil { 152 return changeInfoSlice, errors.Errorf("No version found for task %v with version id %v", 153 task.Id, task.Version) 154 } 155 changeInfo := constructChangeInfo(v, key) 156 changeInfo.Pushtime = task.PushTime.Format(time.RFC850) 157 changeInfoSlice = append(changeInfoSlice, *changeInfo) 158 } 159 160 return changeInfoSlice, nil 161 }