From 31b319d25c0e6b357806e26fe2c621cb91d7f2db Mon Sep 17 00:00:00 2001 From: Alan Bundy Date: Wed, 15 Feb 2017 23:34:13 -0500 Subject: [PATCH 1/4] Initial working version. The notifiers/email/receivers changed from taking a list to having nested keys with a list for their values. The NotifProfile was changed to use the new receiver types as well. See the updated README.md file for information. --- README.md | 72 +++++++++--- check-handler.go | 29 ++--- consul/client.go | 44 +++++-- consul/interface.go | 11 +- notifier/email-notifier.go | 200 ++++++++++++++++++-------------- notifier/email-notifier_test.go | 7 +- notifier/notifier.go | 25 ++-- 7 files changed, 245 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index 62ce3504..a70d8516 100644 --- a/README.md +++ b/README.md @@ -192,6 +192,11 @@ Ex. `emailer_only` would be located at `consul-alerts/config/notif-profiles/emai "patternProperties" : { ".{1,}" : { "type" : "string" } } + }, + "NotifTypeList": { + "type": "object", + "title": "Hash of types for a given Notifier.", + "description": "A listing of Notifier names with a listing value indicating the types for that Notifier to use. (e.g. EmailNotifier can have receiver types like 'admins' that refer to a list of email addresses)" } }, "required": [ @@ -213,6 +218,9 @@ Ex. `emailer_only` would be located at `consul-alerts/config/notif-profiles/emai "NotifList": { "log":false, "email":true + }, + "NotifTypeList": { + "email": ["admins", "users"] } } ``` @@ -299,20 +307,56 @@ The email and smtp details needs to be configured: prefix: `consul-alerts/config/notifiers/email/` -| key | description | -|--------------|----------------------------------------------------------------------------------| -| enabled | Enable the email notifier. [Default: false] | -| cluster-name | The name of the cluster. [Default: "Consul Alerts"] | -| url | The SMTP server url | -| port | The SMTP server port | -| username | The SMTP username | -| password | The SMTP password | -| sender-alias | The sender alias. [Default: "Consul Alerts"] | -| sender-email | The sender email | -| receivers | The emails of the receivers. JSON array of string | -| template | Path to custom email template. [Default: internal template] | -| one-per-alert| Whether to send one email per alert [Default: false] | -| one-per-node | Whether to send one email per node [Default: false] (overriden by one-per-alert) | +| key | description | +|--------------|-------------------------------------------------------------------------------------------------------| +| enabled | Enable the email notifier. [Default: false] | +| cluster-name | The name of the cluster. [Default: "Consul Alerts"] | +| url | The SMTP server url | +| port | The SMTP server port | +| username | The SMTP username | +| password | The SMTP password | +| sender-alias | The sender alias. [Default: "Consul Alerts"] | +| sender-email | The sender email | +| receivers | Types of email receivers and associated email addresses. Expanded below since receivers is a folder. | +| template | Path to custom email template. [Default: internal template] | +| one-per-alert| Whether to send one email per alert [Default: false] | +| one-per-node | Whether to send one email per node [Default: false] (overriden by one-per-alert) | + +This email receivers configuration allows custom keys nested under: + +prefix: `consul-alerts/config/notifiers/email/receivers/` + +|key | description | +|------------|-------------------------------------------------------------------------------------------------------------------------------------| +| any-string | A list of email addresses that will be used when the corresponding key is used in the NotifTypeList of a NotifProfile. JSON array. | +| any-string | Another list of email addresses as above. Any number of such key/value pairs can be nested under receivers. See example below. | + +**Example email configuration:** + +**Key:** `consul-alerts/config/notifiers/emailer/` + +**Value:** +``` +{ + "enabled": true, + "url": "smtp.example.com", + "port": 587, + "username": "someuser", + "password": "password1", + "sender-email": "no-reply@example.com", + "receivers/" +} +``` + +**Example email receivers configuration (note the keys can be whatever you want, and you refer to the keys in NotifProfile under NotifTypeList):** +**Key:** `consul-alerts/config/notifiers/emailer/receivers/` +``` +{ + "admins": ["admin1@example.com", "admin2@example.com"], + "users": ["user1@ample.com", "user2@example.com"], + "any-collection-of-emails-you-want": ["a@example.com", "b@example.com", "c@example.com"] +} +``` The template can be any go html template. An `EmailData` instance will be passed to the template. diff --git a/check-handler.go b/check-handler.go index a4bd4346..f149daac 100644 --- a/check-handler.go +++ b/check-handler.go @@ -108,22 +108,23 @@ func (c *CheckProcessor) handleChecks(checks []consul.Check) { func (c *CheckProcessor) notify(alerts []consul.Check) { messages := make([]notifier.Message, len(alerts)) for i, alert := range alerts { - notifMap, interval := consulClient.GetProfileInfo(alert.Node, alert.ServiceID, alert.CheckID) + profileInfo := consulClient.GetProfileInfo(alert.Node, alert.ServiceID, alert.CheckID) messages[i] = notifier.Message{ - Node: alert.Node, - ServiceId: alert.ServiceID, - Service: alert.ServiceName, - CheckId: alert.CheckID, - Check: alert.Name, - Status: alert.Status, - Output: alert.Output, - Notes: alert.Notes, - Interval: interval, - RmdCheck: time.Now(), - NotifList: notifMap, - Timestamp: time.Now(), + Node: alert.Node, + ServiceId: alert.ServiceID, + Service: alert.ServiceName, + CheckId: alert.CheckID, + Check: alert.Name, + Status: alert.Status, + Output: alert.Output, + Notes: alert.Notes, + Interval: profileInfo.Interval, + RmdCheck: time.Now(), + NotifList: profileInfo.NotifList, + NotifTypeList: profileInfo.NotifTypeList, + Timestamp: time.Now(), } - if interval > 0 { + if profileInfo.Interval > 0 { switch alert.Status { case "passing": consulClient.DeleteReminder(alert.Node, alert.CheckID) diff --git a/consul/client.go b/consul/client.go index 5afe93c6..9e633da4 100644 --- a/consul/client.go +++ b/consul/client.go @@ -21,6 +21,7 @@ const ( ConfigTypeString ConfigTypeInt ConfigTypeStrArray + ConfigTypeStrMap ) type configType int @@ -99,8 +100,16 @@ func (c *ConsulAlertClient) LoadConfig() { valErr = loadCustomValue(&config.Notifiers.Email.Password, val, ConfigTypeString) case "consul-alerts/config/notifiers/email/port": valErr = loadCustomValue(&config.Notifiers.Email.Port, val, ConfigTypeInt) - case "consul-alerts/config/notifiers/email/receivers": - valErr = loadCustomValue(&config.Notifiers.Email.Receivers, val, ConfigTypeStrArray) + case "consul-alerts/config/notifiers/email/receivers/": + kvmTemp := c.KvMap("consul-alerts/config/notifiers/email/receivers") + // only want the key at the end, so split on slashes and take the last item + kvm := make(map[string][]string, len(kvmTemp)) + for k, v := range kvmTemp { + kSplit := strings.Split(k, "/") + kvm[kSplit[len(kSplit)-1]] = v + } + convertedVal, _ := json.Marshal(kvm) + valErr = loadCustomValue(&config.Notifiers.Email.Receivers, convertedVal, ConfigTypeStrMap) case "consul-alerts/config/notifiers/email/sender-alias": valErr = loadCustomValue(&config.Notifiers.Email.SenderAlias, val, ConfigTypeString) case "consul-alerts/config/notifiers/email/sender-email": @@ -232,6 +241,9 @@ func loadCustomValue(configVariable interface{}, data []byte, cType configType) case ConfigTypeStrArray: arrConfig := configVariable.(*[]string) err = json.Unmarshal(data, arrConfig) + case ConfigTypeStrMap: + mapConfig := configVariable.(*map[string][]string) + err = json.Unmarshal(data, mapConfig) } return err } @@ -275,11 +287,10 @@ func (c *ConsulAlertClient) UpdateCheckData() { } if settodelete { log.Printf("Reminder %s %s needs to be deleted, stale", node, check) - c.DeleteReminder(node, check) + c.DeleteReminder(node, check) } } - for _, health := range healths { node := health.Node @@ -407,6 +418,22 @@ func (c *ConsulAlertClient) CustomNotifiers() (customNotifs map[string]string) { return customNotifs } +// KvMap returns a map of KV pairs found directly inside the passed path +func (c *ConsulAlertClient) KvMap(kvPath string) (kvMap map[string][]string) { + if kvPairs, _, err := c.api.KV().List(kvPath, nil); err == nil { + kvMap = make(map[string][]string) + for _, kvPair := range kvPairs { + if strings.HasSuffix(kvPair.Key, "/") { + continue + } + itemList := []string{} + json.Unmarshal(kvPair.Value, &itemList) + kvMap[string(kvPair.Key)] = itemList + } + } + return kvMap +} + func (c *ConsulAlertClient) NewAlertsWithFilter(nodeName string, serviceName string, checkName string, statuses []string, ignoreBlacklist bool) []Check { allChecks, _, _ := c.api.KV().List("consul-alerts/checks", nil) alerts := make([]Check, 0) @@ -619,7 +646,7 @@ func (c *ConsulAlertClient) CheckStatus(node, serviceId, checkId string) (status } // GetProfileInfo returns profile info for check -func (c *ConsulAlertClient) GetProfileInfo(node, serviceID, checkID string) (notifiersList map[string]bool, interval int) { +func (c *ConsulAlertClient) GetProfileInfo(node, serviceID, checkID string) (profileInfo ProfileInfo) { log.Println("Getting profile for node: ", node, " service: ", serviceID, " check: ", checkID) var profile string @@ -645,12 +672,7 @@ func (c *ConsulAlertClient) GetProfileInfo(node, serviceID, checkID string) (not log.Println("profile key not found.") return } - var checkProfile ProfileInfo - json.Unmarshal(kvPair.Value, &checkProfile) - - notifiersList = checkProfile.NotifList - interval = checkProfile.Interval - log.Println("Interval: ", interval, " List: ", notifiersList) + json.Unmarshal(kvPair.Value, &profileInfo) return } diff --git a/consul/interface.go b/consul/interface.go index 60dc6f39..d671a582 100644 --- a/consul/interface.go +++ b/consul/interface.go @@ -67,7 +67,7 @@ type EmailNotifierConfig struct { Password string SenderAlias string SenderEmail string - Receivers []string + Receivers map[string][]string Template string OnePerAlert bool OnePerNode bool @@ -144,8 +144,9 @@ type Status struct { // ProfileInfo is for reading in JSON from profile keys type ProfileInfo struct { - Interval int - NotifList map[string]bool + Interval int + NotifList map[string]bool + NotifTypeList map[string][]string } // Consul interface provides access to consul client @@ -178,7 +179,7 @@ type Consul interface { CheckStatus(node, statusId, checkId string) (status, output string) CheckKeyExists(key string) bool - GetProfileInfo(node, serviceID, checkID string) (notifiersList map[string]bool, interval int) + GetProfileInfo(node, serviceID, checkID string) (profileInfo ProfileInfo) GetReminders() []notifier.Message SetReminder(m notifier.Message) @@ -202,7 +203,7 @@ func DefaultAlertConfig() *ConsulAlertConfig { ClusterName: "Consul-Alerts", Enabled: false, SenderAlias: "Consul Alerts", - Receivers: []string{}, + Receivers: map[string][]string{}, } log := &LogNotifierConfig{ diff --git a/notifier/email-notifier.go b/notifier/email-notifier.go index 8a2e55a4..b18faa89 100644 --- a/notifier/email-notifier.go +++ b/notifier/email-notifier.go @@ -22,7 +22,7 @@ type EmailNotifier struct { Password string SenderAlias string SenderEmail string - Receivers []string + Receivers map[string][]string NotifName string OnePerAlert bool OnePerNode bool @@ -57,93 +57,120 @@ func (emailNotifier *EmailNotifier) NotifierName() string { //Notify sends messages to the endpoint notifier func (emailNotifier *EmailNotifier) Notify(alerts Messages) bool { - overAllStatus, pass, warn, fail := alerts.Summary() - nodeMap := mapByNodes(alerts) + // Get a unique list of all email NotifTypeList values found in Messages. + // These should correspond to the keys nested under notifiers/email/receivers/ + emailTypes := make(map[string]bool) + for _, alert := range alerts { + for _, emailType := range alert.NotifTypeList["email"] { + emailTypes[emailType] = true + } + } - var emailDataList []EmailData + success := true - if emailNotifier.OnePerAlert { - log.Println("Going to send one email per alert") - emailDataList = []EmailData{} - for _, check := range alerts { + // Filter on each email receiver type, and send emails + for emailType, _ := range emailTypes { + + // Filter the Messages to the ones with the given emailType + filteredAlerts := make(Messages, 0) + for _, alert := range alerts { + for _, nt := range alert.NotifTypeList["email"] { + if nt == emailType { + filteredAlerts = append(filteredAlerts, alert) + break + } + } + } - singleAlertChecks := make(Messages, 0) - singleAlertChecks = append(singleAlertChecks, check) - singleAlertMap := mapByNodes(singleAlertChecks) + // Filter the Receivers to the ones with the given emailType + emailTo := emailNotifier.Receivers[emailType] - alertStatus, alertPassing, alertWarnings, alertFailures := singleAlertChecks.Summary() + overAllStatus, pass, warn, fail := filteredAlerts.Summary() + nodeMap := mapByNodes(filteredAlerts) - alertClusterName := emailNotifier.ClusterName + " " + check.Node + " - " + check.CheckId + var emailDataList []EmailData - e := EmailData{ - ClusterName: alertClusterName, - SystemStatus: alertStatus, - FailCount: alertFailures, - WarnCount: alertWarnings, - PassCount: alertPassing, - Nodes: singleAlertMap, - } - emailDataList = append(emailDataList, e) - } - } else if emailNotifier.OnePerNode { - log.Println("Going to send one email per node") - emailDataList = []EmailData{} - for nodeName, checks := range nodeMap { - singleNodeMap := mapByNodes(checks) - nodeStatus, nodePassing, nodeWarnings, nodeFailures := checks.Summary() + if emailNotifier.OnePerAlert { + log.Println("Going to send one email per alert") + emailDataList = []EmailData{} + for _, check := range filteredAlerts { - nodeClusterName := emailNotifier.ClusterName + " " + nodeName + singleAlertChecks := make(Messages, 0) + singleAlertChecks = append(singleAlertChecks, check) + singleAlertMap := mapByNodes(singleAlertChecks) - e := EmailData{ - ClusterName: nodeClusterName, - SystemStatus: nodeStatus, - FailCount: nodeFailures, - WarnCount: nodeWarnings, - PassCount: nodePassing, - Nodes: singleNodeMap, - } - emailDataList = append(emailDataList, e) - } - } else { - log.Println("Going to send one email for many alerts") - e := EmailData{ - ClusterName: emailNotifier.ClusterName, - SystemStatus: overAllStatus, - FailCount: fail, - WarnCount: warn, - PassCount: pass, - Nodes: nodeMap, - } + alertStatus, alertPassing, alertWarnings, alertFailures := singleAlertChecks.Summary() - emailDataList = []EmailData{e} - } + alertClusterName := emailNotifier.ClusterName + " " + check.Node + " - " + check.CheckId - success := true + e := EmailData{ + ClusterName: alertClusterName, + SystemStatus: alertStatus, + FailCount: alertFailures, + WarnCount: alertWarnings, + PassCount: alertPassing, + Nodes: singleAlertMap, + } + emailDataList = append(emailDataList, e) + } + } else if emailNotifier.OnePerNode { + log.Println("Going to send one email per node") + emailDataList = []EmailData{} + for nodeName, checks := range nodeMap { + singleNodeMap := mapByNodes(checks) + nodeStatus, nodePassing, nodeWarnings, nodeFailures := checks.Summary() + + nodeClusterName := emailNotifier.ClusterName + " " + nodeName + + e := EmailData{ + ClusterName: nodeClusterName, + SystemStatus: nodeStatus, + FailCount: nodeFailures, + WarnCount: nodeWarnings, + PassCount: nodePassing, + Nodes: singleNodeMap, + } + emailDataList = append(emailDataList, e) + } + } else { + log.Println("Going to send one email for many alerts") - for _, e := range emailDataList { + e := EmailData{ + ClusterName: emailNotifier.ClusterName, + SystemStatus: overAllStatus, + FailCount: fail, + WarnCount: warn, + PassCount: pass, + Nodes: nodeMap, + } - var tmpl *template.Template - var err error - if emailNotifier.Template == "" { - tmpl, err = template.New("base").Parse(defaultTemplate) - } else { - tmpl, err = template.ParseFiles(emailNotifier.Template) + emailDataList = []EmailData{e} } - if err != nil { - log.Println("Template error, unable to send email notification: ", err) - success = false - continue - } + for _, e := range emailDataList { - var body bytes.Buffer - if err := tmpl.Execute(&body, e); err != nil { - log.Println("Template error, unable to send email notification: ", err) - success = false - continue - } + var tmpl *template.Template + var err error + if emailNotifier.Template == "" { + tmpl, err = template.New("base").Parse(defaultTemplate) + } else { + tmpl, err = template.ParseFiles(emailNotifier.Template) + } + + if err != nil { + log.Println("Template error, unable to send email notification: ", err) + success = false + continue + } - msg := fmt.Sprintf(`From: "%s" <%s> + var body bytes.Buffer + if err := tmpl.Execute(&body, e); err != nil { + log.Println("Template error, unable to send email notification: ", err) + success = false + continue + } + + msg := fmt.Sprintf(`From: "%s" <%s> To: %s Subject: %s is %s MIME-version: 1.0; @@ -151,21 +178,22 @@ Content-Type: text/html; charset="UTF-8"; %s `, - emailNotifier.SenderAlias, - emailNotifier.SenderEmail, - strings.Join(emailNotifier.Receivers, ", "), - e.ClusterName, - e.SystemStatus, - body.String()) - - addr := fmt.Sprintf("%s:%d", emailNotifier.Url, emailNotifier.Port) - auth := smtp.PlainAuth("", emailNotifier.Username, emailNotifier.Password, emailNotifier.Url) - if err := sendMail(addr, auth, emailNotifier.SenderEmail, emailNotifier.Receivers, []byte(msg)); err != nil { - log.Println("Unable to send notification:", err) - continue + emailNotifier.SenderAlias, + emailNotifier.SenderEmail, + strings.Join(emailTo, ", "), + e.ClusterName, + e.SystemStatus, + body.String()) + + addr := fmt.Sprintf("%s:%d", emailNotifier.Url, emailNotifier.Port) + auth := smtp.PlainAuth("", emailNotifier.Username, emailNotifier.Password, emailNotifier.Url) + if err := sendMail(addr, auth, emailNotifier.SenderEmail, emailTo, []byte(msg)); err != nil { + log.Println("Unable to send notification:", err) + continue + } + log.Println("Email notification sent.") + success = success && true } - log.Println("Email notification sent.") - success = success && true } return success diff --git a/notifier/email-notifier_test.go b/notifier/email-notifier_test.go index 525e8130..a5b2b8a0 100644 --- a/notifier/email-notifier_test.go +++ b/notifier/email-notifier_test.go @@ -19,7 +19,12 @@ func TestNotify(t *testing.T) { expectedAddr := fmt.Sprintf("%s:%d", host, port) expectedFrom := "sender@example.com" - expectedTo := []string{"test1@example.com", "test2@example.com"} + expectedTo := make(map[string][]string) + admins := []string{"testadmin1@example.com", "testadmin2@example.com"} + users := []string{"testuser1@example.com", "testuser2@example.com"} + expectedTo["admins"] = admins + expectedTo["users"] = users + expectedMsg := `From: "Some Sender" To: test1@example.com, test2@example.com Subject: Some Cluster is HEALTHY diff --git a/notifier/notifier.go b/notifier/notifier.go index e853bab0..c8dbe442 100644 --- a/notifier/notifier.go +++ b/notifier/notifier.go @@ -15,18 +15,19 @@ Fail: %d, Warn: %d, Pass: %d ` type Message struct { - Node string - ServiceId string - Service string - CheckId string - Check string - Status string - Output string - Notes string - Interval int - RmdCheck time.Time - NotifList map[string]bool - Timestamp time.Time + Node string + ServiceId string + Service string + CheckId string + Check string + Status string + Output string + Notes string + Interval int + RmdCheck time.Time + NotifList map[string]bool + NotifTypeList map[string][]string + Timestamp time.Time } type Messages []Message From fadf0def112bf0db2d35ebc4892104936fa3db6d Mon Sep 17 00:00:00 2001 From: Alan Bundy Date: Thu, 16 Feb 2017 09:27:59 -0500 Subject: [PATCH 2/4] Broke the Notifier function into several smaller functions so it is easier to follow. --- notifier/email-notifier.go | 226 +++++++++++++++++++++---------------- 1 file changed, 126 insertions(+), 100 deletions(-) diff --git a/notifier/email-notifier.go b/notifier/email-notifier.go index b18faa89..891a012d 100644 --- a/notifier/email-notifier.go +++ b/notifier/email-notifier.go @@ -70,107 +70,135 @@ func (emailNotifier *EmailNotifier) Notify(alerts Messages) bool { // Filter on each email receiver type, and send emails for emailType, _ := range emailTypes { + success = success && emailNotifier.notifyByType(alerts, emailType) + } - // Filter the Messages to the ones with the given emailType - filteredAlerts := make(Messages, 0) - for _, alert := range alerts { - for _, nt := range alert.NotifTypeList["email"] { - if nt == emailType { - filteredAlerts = append(filteredAlerts, alert) - break - } + return success +} + +func filterMessagesByType(alerts Messages, emailType string) Messages { + filteredAlerts := make(Messages, 0) + for _, alert := range alerts { + for _, nt := range alert.NotifTypeList["email"] { + if nt == emailType { + filteredAlerts = append(filteredAlerts, alert) + break } } + } + return filteredAlerts +} - // Filter the Receivers to the ones with the given emailType - emailTo := emailNotifier.Receivers[emailType] +func (emailNotifier *EmailNotifier) filterReceiversByType(emailType string) []string { + return emailNotifier.Receivers[emailType] +} - overAllStatus, pass, warn, fail := filteredAlerts.Summary() - nodeMap := mapByNodes(filteredAlerts) +func (emailNotifier *EmailNotifier) buildEmailDataOnePerAlert(filteredAlerts Messages) []EmailData { + emailDataList := []EmailData{} + for _, check := range filteredAlerts { - var emailDataList []EmailData + singleAlertChecks := make(Messages, 0) + singleAlertChecks = append(singleAlertChecks, check) + singleAlertMap := mapByNodes(singleAlertChecks) - if emailNotifier.OnePerAlert { - log.Println("Going to send one email per alert") - emailDataList = []EmailData{} - for _, check := range filteredAlerts { + alertStatus, alertPassing, alertWarnings, alertFailures := singleAlertChecks.Summary() - singleAlertChecks := make(Messages, 0) - singleAlertChecks = append(singleAlertChecks, check) - singleAlertMap := mapByNodes(singleAlertChecks) + alertClusterName := emailNotifier.ClusterName + " " + check.Node + " - " + check.CheckId - alertStatus, alertPassing, alertWarnings, alertFailures := singleAlertChecks.Summary() + e := EmailData{ + ClusterName: alertClusterName, + SystemStatus: alertStatus, + FailCount: alertFailures, + WarnCount: alertWarnings, + PassCount: alertPassing, + Nodes: singleAlertMap, + } + emailDataList = append(emailDataList, e) + } + return emailDataList +} - alertClusterName := emailNotifier.ClusterName + " " + check.Node + " - " + check.CheckId +func (emailNotifier *EmailNotifier) buildEmailDataOnePerNode(filteredAlerts Messages, nodeMap map[string]Messages) []EmailData { + emailDataList := []EmailData{} + for nodeName, checks := range nodeMap { + singleNodeMap := mapByNodes(checks) + nodeStatus, nodePassing, nodeWarnings, nodeFailures := checks.Summary() + + nodeClusterName := emailNotifier.ClusterName + " " + nodeName + + e := EmailData{ + ClusterName: nodeClusterName, + SystemStatus: nodeStatus, + FailCount: nodeFailures, + WarnCount: nodeWarnings, + PassCount: nodePassing, + Nodes: singleNodeMap, + } + emailDataList = append(emailDataList, e) + } + return emailDataList +} - e := EmailData{ - ClusterName: alertClusterName, - SystemStatus: alertStatus, - FailCount: alertFailures, - WarnCount: alertWarnings, - PassCount: alertPassing, - Nodes: singleAlertMap, - } - emailDataList = append(emailDataList, e) - } - } else if emailNotifier.OnePerNode { - log.Println("Going to send one email per node") - emailDataList = []EmailData{} - for nodeName, checks := range nodeMap { - singleNodeMap := mapByNodes(checks) - nodeStatus, nodePassing, nodeWarnings, nodeFailures := checks.Summary() - - nodeClusterName := emailNotifier.ClusterName + " " + nodeName - - e := EmailData{ - ClusterName: nodeClusterName, - SystemStatus: nodeStatus, - FailCount: nodeFailures, - WarnCount: nodeWarnings, - PassCount: nodePassing, - Nodes: singleNodeMap, - } - emailDataList = append(emailDataList, e) - } - } else { - log.Println("Going to send one email for many alerts") - - e := EmailData{ - ClusterName: emailNotifier.ClusterName, - SystemStatus: overAllStatus, - FailCount: fail, - WarnCount: warn, - PassCount: pass, - Nodes: nodeMap, - } +func (emailNotifier *EmailNotifier) buildEmailDataOneForManyAlerts(filteredAlerts Messages, nodeMap map[string]Messages) []EmailData { + overAllStatus, pass, warn, fail := filteredAlerts.Summary() + e := EmailData{ + ClusterName: emailNotifier.ClusterName, + SystemStatus: overAllStatus, + FailCount: fail, + WarnCount: warn, + PassCount: pass, + Nodes: nodeMap, + } + return []EmailData{e} +} - emailDataList = []EmailData{e} - } +func (emailNotifier *EmailNotifier) notifyByType(alerts Messages, emailType string) bool { - for _, e := range emailDataList { + success := true - var tmpl *template.Template - var err error - if emailNotifier.Template == "" { - tmpl, err = template.New("base").Parse(defaultTemplate) - } else { - tmpl, err = template.ParseFiles(emailNotifier.Template) - } + filteredAlerts := filterMessagesByType(alerts, emailType) + emailTo := emailNotifier.filterReceiversByType(emailType) - if err != nil { - log.Println("Template error, unable to send email notification: ", err) - success = false - continue - } + //overAllStatus, pass, warn, fail := filteredAlerts.Summary() + nodeMap := mapByNodes(filteredAlerts) - var body bytes.Buffer - if err := tmpl.Execute(&body, e); err != nil { - log.Println("Template error, unable to send email notification: ", err) - success = false - continue - } + var emailDataList []EmailData + + if emailNotifier.OnePerAlert { + log.Println("Going to send one email per alert") + emailDataList = emailNotifier.buildEmailDataOnePerAlert(filteredAlerts) + } else if emailNotifier.OnePerNode { + log.Println("Going to send one email per node") + emailDataList = emailNotifier.buildEmailDataOnePerNode(filteredAlerts, nodeMap) + } else { + log.Println("Going to send one email for many alerts") + emailDataList = emailNotifier.buildEmailDataOneForManyAlerts(filteredAlerts, nodeMap) + } + + for _, e := range emailDataList { - msg := fmt.Sprintf(`From: "%s" <%s> + var tmpl *template.Template + var err error + if emailNotifier.Template == "" { + tmpl, err = template.New("base").Parse(defaultTemplate) + } else { + tmpl, err = template.ParseFiles(emailNotifier.Template) + } + + if err != nil { + log.Println("Template error, unable to send email notification: ", err) + success = false + continue + } + + var body bytes.Buffer + if err := tmpl.Execute(&body, e); err != nil { + log.Println("Template error, unable to send email notification: ", err) + success = false + continue + } + + msg := fmt.Sprintf(`From: "%s" <%s> To: %s Subject: %s is %s MIME-version: 1.0; @@ -178,24 +206,22 @@ Content-Type: text/html; charset="UTF-8"; %s `, - emailNotifier.SenderAlias, - emailNotifier.SenderEmail, - strings.Join(emailTo, ", "), - e.ClusterName, - e.SystemStatus, - body.String()) - - addr := fmt.Sprintf("%s:%d", emailNotifier.Url, emailNotifier.Port) - auth := smtp.PlainAuth("", emailNotifier.Username, emailNotifier.Password, emailNotifier.Url) - if err := sendMail(addr, auth, emailNotifier.SenderEmail, emailTo, []byte(msg)); err != nil { - log.Println("Unable to send notification:", err) - continue - } - log.Println("Email notification sent.") - success = success && true + emailNotifier.SenderAlias, + emailNotifier.SenderEmail, + strings.Join(emailTo, ", "), + e.ClusterName, + e.SystemStatus, + body.String()) + + addr := fmt.Sprintf("%s:%d", emailNotifier.Url, emailNotifier.Port) + auth := smtp.PlainAuth("", emailNotifier.Username, emailNotifier.Password, emailNotifier.Url) + if err := sendMail(addr, auth, emailNotifier.SenderEmail, emailTo, []byte(msg)); err != nil { + log.Println("Unable to send notification:", err) + continue } + log.Println("Email notification sent.") + success = success && true } - return success } From 831d57ead3c53f7bd3da336b778f4776118f1f34 Mon Sep 17 00:00:00 2001 From: Alan Bundy Date: Thu, 16 Feb 2017 10:23:07 -0500 Subject: [PATCH 3/4] Moved functions around in the file to make it a little easier to follow. --- notifier/email-notifier.go | 146 ++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/notifier/email-notifier.go b/notifier/email-notifier.go index 891a012d..d72328d1 100644 --- a/notifier/email-notifier.go +++ b/notifier/email-notifier.go @@ -76,6 +76,79 @@ func (emailNotifier *EmailNotifier) Notify(alerts Messages) bool { return success } +func (emailNotifier *EmailNotifier) notifyByType(alerts Messages, emailType string) bool { + + success := true + + filteredAlerts := filterMessagesByType(alerts, emailType) + emailTo := emailNotifier.filterReceiversByType(emailType) + + //overAllStatus, pass, warn, fail := filteredAlerts.Summary() + nodeMap := mapByNodes(filteredAlerts) + + var emailDataList []EmailData + + if emailNotifier.OnePerAlert { + log.Println("Going to send one email per alert") + emailDataList = emailNotifier.buildEmailDataOnePerAlert(filteredAlerts) + } else if emailNotifier.OnePerNode { + log.Println("Going to send one email per node") + emailDataList = emailNotifier.buildEmailDataOnePerNode(filteredAlerts, nodeMap) + } else { + log.Println("Going to send one email for many alerts") + emailDataList = emailNotifier.buildEmailDataOneForManyAlerts(filteredAlerts, nodeMap) + } + + for _, e := range emailDataList { + + var tmpl *template.Template + var err error + if emailNotifier.Template == "" { + tmpl, err = template.New("base").Parse(defaultTemplate) + } else { + tmpl, err = template.ParseFiles(emailNotifier.Template) + } + + if err != nil { + log.Println("Template error, unable to send email notification: ", err) + success = false + continue + } + + var body bytes.Buffer + if err := tmpl.Execute(&body, e); err != nil { + log.Println("Template error, unable to send email notification: ", err) + success = false + continue + } + + msg := fmt.Sprintf(`From: "%s" <%s> +To: %s +Subject: %s is %s +MIME-version: 1.0; +Content-Type: text/html; charset="UTF-8"; + +%s +`, + emailNotifier.SenderAlias, + emailNotifier.SenderEmail, + strings.Join(emailTo, ", "), + e.ClusterName, + e.SystemStatus, + body.String()) + + addr := fmt.Sprintf("%s:%d", emailNotifier.Url, emailNotifier.Port) + auth := smtp.PlainAuth("", emailNotifier.Username, emailNotifier.Password, emailNotifier.Url) + if err := sendMail(addr, auth, emailNotifier.SenderEmail, emailTo, []byte(msg)); err != nil { + log.Println("Unable to send notification:", err) + continue + } + log.Println("Email notification sent.") + success = success && true + } + return success +} + func filterMessagesByType(alerts Messages, emailType string) Messages { filteredAlerts := make(Messages, 0) for _, alert := range alerts { @@ -152,79 +225,6 @@ func (emailNotifier *EmailNotifier) buildEmailDataOneForManyAlerts(filteredAlert return []EmailData{e} } -func (emailNotifier *EmailNotifier) notifyByType(alerts Messages, emailType string) bool { - - success := true - - filteredAlerts := filterMessagesByType(alerts, emailType) - emailTo := emailNotifier.filterReceiversByType(emailType) - - //overAllStatus, pass, warn, fail := filteredAlerts.Summary() - nodeMap := mapByNodes(filteredAlerts) - - var emailDataList []EmailData - - if emailNotifier.OnePerAlert { - log.Println("Going to send one email per alert") - emailDataList = emailNotifier.buildEmailDataOnePerAlert(filteredAlerts) - } else if emailNotifier.OnePerNode { - log.Println("Going to send one email per node") - emailDataList = emailNotifier.buildEmailDataOnePerNode(filteredAlerts, nodeMap) - } else { - log.Println("Going to send one email for many alerts") - emailDataList = emailNotifier.buildEmailDataOneForManyAlerts(filteredAlerts, nodeMap) - } - - for _, e := range emailDataList { - - var tmpl *template.Template - var err error - if emailNotifier.Template == "" { - tmpl, err = template.New("base").Parse(defaultTemplate) - } else { - tmpl, err = template.ParseFiles(emailNotifier.Template) - } - - if err != nil { - log.Println("Template error, unable to send email notification: ", err) - success = false - continue - } - - var body bytes.Buffer - if err := tmpl.Execute(&body, e); err != nil { - log.Println("Template error, unable to send email notification: ", err) - success = false - continue - } - - msg := fmt.Sprintf(`From: "%s" <%s> -To: %s -Subject: %s is %s -MIME-version: 1.0; -Content-Type: text/html; charset="UTF-8"; - -%s -`, - emailNotifier.SenderAlias, - emailNotifier.SenderEmail, - strings.Join(emailTo, ", "), - e.ClusterName, - e.SystemStatus, - body.String()) - - addr := fmt.Sprintf("%s:%d", emailNotifier.Url, emailNotifier.Port) - auth := smtp.PlainAuth("", emailNotifier.Username, emailNotifier.Password, emailNotifier.Url) - if err := sendMail(addr, auth, emailNotifier.SenderEmail, emailTo, []byte(msg)); err != nil { - log.Println("Unable to send notification:", err) - continue - } - log.Println("Email notification sent.") - success = success && true - } - return success -} - func mapByNodes(alerts Messages) map[string]Messages { nodeMap := make(map[string]Messages) for _, alert := range alerts { From 723bc695a34af251b3929e50617f617f38270128 Mon Sep 17 00:00:00 2001 From: Alan Bundy Date: Tue, 21 Feb 2017 08:57:57 -0500 Subject: [PATCH 4/4] Removed code that should have been deleted during the previous commit (the merge). --- consul/interface.go | 88 --------------------------------------------- 1 file changed, 88 deletions(-) diff --git a/consul/interface.go b/consul/interface.go index 0f71da48..1cd07906 100644 --- a/consul/interface.go +++ b/consul/interface.go @@ -45,94 +45,6 @@ type EventsConfig struct { Handlers []string } -type NotifiersConfig struct { - Email *EmailNotifierConfig - Log *LogNotifierConfig - Influxdb *InfluxdbNotifierConfig - Slack *SlackNotifierConfig - PagerDuty *PagerDutyNotifierConfig - HipChat *HipChatNotifierConfig - OpsGenie *OpsGenieNotifierConfig - AwsSns *AwsSnsNotifierConfig - VictorOps *VictorOpsNotifierConfig - Custom []string -} - -type EmailNotifierConfig struct { - ClusterName string - Enabled bool - Url string - Port int - Username string - Password string - SenderAlias string - SenderEmail string - Receivers map[string][]string - Template string - OnePerAlert bool - OnePerNode bool -} - -type LogNotifierConfig struct { - Enabled bool - Path string -} - -type InfluxdbNotifierConfig struct { - Enabled bool - Host string - Username string - Password string - Database string - SeriesName string -} - -type SlackNotifierConfig struct { - Enabled bool - ClusterName string - Url string - Channel string - Username string - IconUrl string - IconEmoji string - Detailed bool -} - -type PagerDutyNotifierConfig struct { - Enabled bool - ServiceKey string - ClientName string - ClientUrl string -} - -type HipChatNotifierConfig struct { - Enabled bool - ClusterName string - RoomId string - AuthToken string - BaseURL string - From string -} - -type OpsGenieNotifierConfig struct { - Enabled bool - ClusterName string - ApiKey string -} - -type AwsSnsNotifierConfig struct { - Enabled bool - Region string - TopicArn string -} - -// VictorOpsNotifierConfig provides configuration options for VictorOps notifier -type VictorOpsNotifierConfig struct { - Enabled bool - APIKey string - RoutingKey string -} - type Status struct { Current string CurrentTimestamp time.Time