Browse Source

add incident resolved email mechanism

Alexander Belanger 3 years ago
parent
commit
c20466814a

+ 2 - 1
api/server/handlers/cluster/notify_new_incident.go

@@ -74,7 +74,7 @@ func (c *NotifyNewIncidentHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 	notifiers := make([]notifier.IncidentNotifier, 0)
 
 	if c.Config().SlackConf != nil {
-		notifiers = append(notifiers, slack.NewIncidentNotifier(notifConf, slackInts...))
+		notifiers = append(notifiers, slack.NewIncidentNotifier(slackInts...))
 	}
 
 	if sc := c.Config().ServerConf; sc.SendgridAPIKey != "" && sc.SendgridSenderEmail != "" && sc.SendgridIncidentAlertTemplateID != "" {
@@ -89,6 +89,7 @@ func (c *NotifyNewIncidentHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 	}
 
 	multi := notifier.NewMultiIncidentNotifier(
+		notifConf,
 		notifiers...,
 	)
 

+ 31 - 2
api/server/handlers/cluster/notify_resolved_incident.go

@@ -11,6 +11,8 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
 	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/internal/notifier"
+	"github.com/porter-dev/porter/internal/notifier/sendgrid"
 	"github.com/porter-dev/porter/internal/notifier/slack"
 )
 
@@ -61,10 +63,37 @@ func (c *NotifyResolvedIncidentHandler) ServeHTTP(w http.ResponseWriter, r *http
 		notifConf = conf.ToNotificationConfigType()
 	}
 
-	notifier := slack.NewIncidentNotifier(notifConf, slackInts...)
+	notifiers := make([]notifier.IncidentNotifier, 0)
+
+	if c.Config().SlackConf != nil {
+		notifiers = append(notifiers, slack.NewIncidentNotifier(slackInts...))
+	}
+
+	if sc := c.Config().ServerConf; sc.SendgridAPIKey != "" && sc.SendgridSenderEmail != "" && sc.SendgridIncidentAlertTemplateID != "" {
+		users, err := getUsersByProjectID(c.Repo(), cluster.ProjectID)
+
+		if err != nil {
+			c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
+			return
+		}
+
+		notifiers = append(notifiers, sendgrid.NewIncidentNotifier(&sendgrid.IncidentNotifierOpts{
+			SharedOpts: &sendgrid.SharedOpts{
+				APIKey:      c.Config().ServerConf.SendgridAPIKey,
+				SenderEmail: c.Config().ServerConf.SendgridSenderEmail,
+			},
+			IncidentResolvedTemplateID: sc.SendgridIncidentResolvedTemplateID,
+			Users:                      users,
+		}))
+	}
+
+	multi := notifier.NewMultiIncidentNotifier(
+		notifConf,
+		notifiers...,
+	)
 
 	if !cluster.NotificationsDisabled {
-		err := notifier.NotifyResolved(
+		err := multi.NotifyResolved(
 			request, fmt.Sprintf(
 				"%s/cluster-dashboard/incidents/%s?namespace=%s",
 				c.Config().ServerConf.ServerURL,

+ 15 - 2
internal/notifier/incident_notifier.go

@@ -8,14 +8,21 @@ type IncidentNotifier interface {
 }
 
 type MultiIncidentNotifier struct {
+	notifConf *types.NotificationConfig
 	notifiers []IncidentNotifier
 }
 
-func NewMultiIncidentNotifier(notifiers ...IncidentNotifier) IncidentNotifier {
-	return &MultiIncidentNotifier{notifiers}
+func NewMultiIncidentNotifier(notifConf *types.NotificationConfig, notifiers ...IncidentNotifier) IncidentNotifier {
+	return &MultiIncidentNotifier{notifConf, notifiers}
 }
 
 func (m *MultiIncidentNotifier) NotifyNew(incident *types.Incident, url string) error {
+	// if notification config exists and notifs are disabled for this release, or failure notifications
+	// are disabled, do not alert
+	if m.notifConf != nil && (!m.notifConf.Enabled || !m.notifConf.Failure) {
+		return nil
+	}
+
 	for _, n := range m.notifiers {
 		if err := n.NotifyNew(incident, url); err != nil {
 			return err
@@ -26,6 +33,12 @@ func (m *MultiIncidentNotifier) NotifyNew(incident *types.Incident, url string)
 }
 
 func (m *MultiIncidentNotifier) NotifyResolved(incident *types.Incident, url string) error {
+	// if notification config exists and notifs are disabled for this release, or failure notifications
+	// are disabled, do not alert
+	if m.notifConf != nil && (!m.notifConf.Enabled || !m.notifConf.Failure) {
+		return nil
+	}
+
 	for _, n := range m.notifiers {
 		if err := n.NotifyResolved(incident, url); err != nil {
 			return err

+ 38 - 3
internal/notifier/sendgrid/incident_notifier.go

@@ -40,7 +40,7 @@ func (s *IncidentNotifier) NotifyNew(incident *types.Incident, url string) error
 	sgMail := &mail.SGMailV3{
 		Personalizations: []*mail.Personalization{
 			{
-				To: addrs,
+				BCC: addrs,
 				DynamicTemplateData: map[string]interface{}{
 					"incident_text": incident.Summary,
 					"app_url":       url,
@@ -52,7 +52,7 @@ func (s *IncidentNotifier) NotifyNew(incident *types.Incident, url string) error
 		},
 		From: &mail.Email{
 			Address: s.opts.SenderEmail,
-			Name:    "Porter",
+			Name:    "Porter Notifications",
 		},
 		TemplateID: s.opts.IncidentAlertTemplateID,
 	}
@@ -65,5 +65,40 @@ func (s *IncidentNotifier) NotifyNew(incident *types.Incident, url string) error
 }
 
 func (s *IncidentNotifier) NotifyResolved(incident *types.Incident, url string) error {
-	return nil
+	request := sendgrid.GetRequest(s.opts.APIKey, "/v3/mail/send", "https://api.sendgrid.com")
+	request.Method = "POST"
+
+	addrs := make([]*mail.Email, 0)
+
+	for _, user := range s.opts.Users {
+		addrs = append(addrs, &mail.Email{
+			Address: user.Email,
+		})
+	}
+
+	sgMail := &mail.SGMailV3{
+		Personalizations: []*mail.Personalization{
+			{
+				BCC: addrs,
+				DynamicTemplateData: map[string]interface{}{
+					"incident_resolved_text": fmt.Sprintf("[Resolved] The incident for application %s has been resolved. The incident text was:\n\n:%s", incident.ReleaseName, incident.Summary),
+					"app_url":                url,
+					"subject":                fmt.Sprintf("[Resolved] The incident for application %s has been resolved", incident.ReleaseName),
+					"preheader":              incident.Summary,
+					"resolved_at":            fmt.Sprintf("%v", incident.UpdatedAt),
+				},
+			},
+		},
+		From: &mail.Email{
+			Address: s.opts.SenderEmail,
+			Name:    "Porter Notifications",
+		},
+		TemplateID: s.opts.IncidentResolvedTemplateID,
+	}
+
+	request.Body = mail.GetRequestBody(sgMail)
+
+	_, err := sendgrid.API(request)
+
+	return err
 }

+ 1 - 3
internal/notifier/slack/incident_notifier.go

@@ -13,13 +13,11 @@ import (
 
 type IncidentNotifier struct {
 	slackInts []*integrations.SlackIntegration
-	Config    *types.NotificationConfig
 }
 
-func NewIncidentNotifier(conf *types.NotificationConfig, slackInts ...*integrations.SlackIntegration) *IncidentNotifier {
+func NewIncidentNotifier(slackInts ...*integrations.SlackIntegration) *IncidentNotifier {
 	return &IncidentNotifier{
 		slackInts: slackInts,
-		Config:    conf,
 	}
 }