| // notifier takes POST'd JSON requests from various sources, such as Prometheus |
| // AlertManager and turns them into outgoing emails. |
| package main |
| |
| import ( |
| "flag" |
| "fmt" |
| "io/ioutil" |
| "net/http" |
| "time" |
| |
| "github.com/gorilla/mux" |
| "go.skia.org/infra/go/chatbot" |
| "go.skia.org/infra/go/common" |
| "go.skia.org/infra/go/email" |
| "go.skia.org/infra/go/httputils" |
| "go.skia.org/infra/go/sklog" |
| "go.skia.org/infra/prometheus/go/alertmanager" |
| ) |
| |
| const ( |
| FROM_ADDRESS = "alertserver@skia.org" |
| ) |
| |
| // flags |
| var ( |
| emailClientSecretFile = flag.String("email_client_secret_file", "/etc/alertmanger_webhooks/email/client_secret.json", "OAuth client secret JSON file for sending email.") |
| emailTokenCacheFile = flag.String("email_token_cache_file", "/var/alertmanger_webhooks/client_token.json", "OAuth token cache file for sending email.") |
| chatWebHooksFile = flag.String("chat_webhooks_file", "/etc/alertmanager_webhooks/chat/chat_config.txt", "Chat webhook config.") |
| local = flag.Bool("local", false, "Running locally, not in prod.") |
| port = flag.String("port", ":8000", "HTTP service port (e.g., ':8001')") |
| promPort = flag.String("prom_port", ":20000", "Metrics service address (e.g., ':10110')") |
| ) |
| |
| var ( |
| emailAuth *email.GMail |
| |
| chatBotConfigReader chatbot.ConfigReader |
| ) |
| |
| // emailHandler accepts incoming JSON encoded alertmanager.AlertManagerRequest's and sends |
| // emails based off that content. |
| // |
| // Email addresses are supplied as query parameters, and there can be more than |
| // one, i.e. ?email=foo@example.com&email=bar@example.org. |
| func emailHandler(w http.ResponseWriter, r *http.Request) { |
| to := r.URL.Query()["email"] |
| sklog.Infof("Sending to: %q", to) |
| if len(to) == 0 { |
| httputils.ReportError(w, r, fmt.Errorf("Missing email addresses in URL: %q", r.RequestURI), "Email addresses missing.") |
| return |
| } |
| body, subject, err := alertmanager.Email(r.Body) |
| if err != nil { |
| httputils.ReportError(w, r, err, "Failed to generate an outgoing email.") |
| return |
| } |
| |
| if err := emailAuth.Send(FROM_ADDRESS, to, subject, body); err != nil { |
| httputils.ReportError(w, r, err, "Failed to send outgoing email.") |
| return |
| } |
| } |
| |
| // chatHandler accepts incoming JSON encoded alertmanager.AlertManagerRequest's and sends |
| // messages based off that content to the chat webhook proxy. |
| // |
| // The query parameter 'room' should indicate the room to send the message to. |
| func chatHandler(w http.ResponseWriter, r *http.Request) { |
| // Parse the chat room name out of the url query params, i.e. ?room=skiabot_alerts. |
| to := r.URL.Query().Get("room") |
| if to == "" { |
| httputils.ReportError(w, r, fmt.Errorf("Missing room in URL: %q", r.RequestURI), "Chat room name missing.") |
| return |
| } |
| |
| // Compose the message. |
| body, err := alertmanager.Chat(r.Body) |
| if err != nil { |
| httputils.ReportError(w, r, err, "Failed to generate an outgoing chat.") |
| return |
| } |
| |
| year, week := time.Now().ISOWeek() |
| threadId := fmt.Sprintf("%d-%d", week, year) |
| |
| // Send the message to the chat room. |
| if err := chatbot.SendUsingConfig(body, to, threadId, chatBotConfigReader); err != nil { |
| httputils.ReportError(w, r, err, "Failed to send outgoing chat.") |
| return |
| } |
| } |
| |
| type ClientConfig struct { |
| ClientID string `json:"client_id"` |
| ClientSecret string `json:"client_secret"` |
| } |
| |
| type Installed struct { |
| Installed ClientConfig `json:"installed"` |
| } |
| |
| func main() { |
| common.InitWithMust( |
| "notifier", |
| common.PrometheusOpt(promPort), |
| common.MetricsLoggingOpt(), |
| ) |
| |
| // Init chatbot. |
| chatbot.Init("AlertManager") |
| chatBotConfigReader = func() string { |
| if b, err := ioutil.ReadFile(*chatWebHooksFile); err != nil { |
| sklog.Errorf("Failed to read chat config %q: %s", *chatWebHooksFile, err) |
| return "" |
| } else { |
| return string(b) |
| } |
| } |
| var err error |
| emailAuth, err = email.NewFromFiles(*emailTokenCacheFile, *emailClientSecretFile) |
| if err != nil { |
| sklog.Fatalf("Failed to create email auth: %v", err) |
| } |
| |
| router := mux.NewRouter() |
| router.HandleFunc("/email", emailHandler).Methods("POST") |
| router.HandleFunc("/chat", chatHandler).Methods("POST") |
| router.HandleFunc("/healthz", httputils.HealthCheckHandler).Methods("GET") |
| http.Handle("/", httputils.LoggingGzipRequestResponse(router)) |
| sklog.Infoln("Ready to serve.") |
| sklog.Fatal(http.ListenAndServe(*port, nil)) |
| } |