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  }