parent
b0fbd85592
commit
21671ca374
@ -1,139 +0,0 @@ |
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package prober |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/json" |
||||
"errors" |
||||
"expvar" |
||||
"fmt" |
||||
"io" |
||||
"net/http" |
||||
"os" |
||||
"strings" |
||||
"time" |
||||
|
||||
"github.com/google/uuid" |
||||
"tailscale.com/util/httpm" |
||||
) |
||||
|
||||
var ( |
||||
alertGenerated = expvar.NewInt("alert_generated") |
||||
alertFailed = expvar.NewInt("alert_failed") |
||||
warningGenerated = expvar.NewInt("warning_generated") |
||||
warningFailed = expvar.NewInt("warning_failed") |
||||
) |
||||
|
||||
// SendAlert sends an alert to the incident response system, to
|
||||
// page a human responder immediately.
|
||||
// summary should be short and state the nature of the emergency.
|
||||
// details can be longer, up to 29 KBytes.
|
||||
func SendAlert(summary, details string) error { |
||||
type squadcastAlert struct { |
||||
Message string `json:"message"` |
||||
Description string `json:"description"` |
||||
Tags map[string]string `json:"tags,omitempty"` |
||||
Status string `json:"status"` |
||||
EventId string `json:"event_id"` |
||||
} |
||||
|
||||
sqa := squadcastAlert{ |
||||
Message: summary, |
||||
Description: details, |
||||
Tags: map[string]string{"severity": "critical"}, |
||||
Status: "trigger", |
||||
EventId: uuid.New().String(), |
||||
} |
||||
|
||||
sqBody, err := json.Marshal(sqa) |
||||
if err != nil { |
||||
alertFailed.Add(1) |
||||
return fmt.Errorf("encoding alert payload: %w", err) |
||||
} |
||||
|
||||
webhookUrl := os.Getenv("SQUADCAST_WEBHOOK") |
||||
if webhookUrl == "" { |
||||
warningFailed.Add(1) |
||||
return errors.New("no SQUADCAST_WEBHOOK configured") |
||||
} |
||||
|
||||
req, err := http.NewRequest(httpm.POST, webhookUrl, bytes.NewBuffer(sqBody)) |
||||
if err != nil { |
||||
alertFailed.Add(1) |
||||
return err |
||||
} |
||||
|
||||
req.Header.Set("Content-Type", "application/json") |
||||
|
||||
client := &http.Client{Timeout: 10 * time.Second} |
||||
resp, err := client.Do(req) |
||||
if err != nil { |
||||
alertFailed.Add(1) |
||||
return err |
||||
} |
||||
defer resp.Body.Close() |
||||
|
||||
if resp.StatusCode != 200 { |
||||
alertFailed.Add(1) |
||||
return errors.New(resp.Status) |
||||
} |
||||
|
||||
body, _ := io.ReadAll(resp.Body) |
||||
if string(body) != "ok" { |
||||
alertFailed.Add(1) |
||||
return errors.New("non-ok response returned from Squadcast") |
||||
} |
||||
|
||||
alertGenerated.Add(1) |
||||
return nil |
||||
} |
||||
|
||||
// SendWarning will post a message to Slack.
|
||||
// details should be a description of the issue.
|
||||
func SendWarning(details string) error { |
||||
webhookUrl := os.Getenv("SLACK_WEBHOOK") |
||||
if webhookUrl == "" { |
||||
warningFailed.Add(1) |
||||
return errors.New("no SLACK_WEBHOOK configured") |
||||
} |
||||
|
||||
type slackRequestBody struct { |
||||
Text string `json:"text"` |
||||
} |
||||
|
||||
slackBody, err := json.Marshal(slackRequestBody{Text: details}) |
||||
if err != nil { |
||||
warningFailed.Add(1) |
||||
return err |
||||
} |
||||
|
||||
req, err := http.NewRequest("POST", webhookUrl, bytes.NewReader(slackBody)) |
||||
if err != nil { |
||||
warningFailed.Add(1) |
||||
return err |
||||
} |
||||
req.Header.Set("Content-Type", "application/json") |
||||
|
||||
client := &http.Client{Timeout: 10 * time.Second} |
||||
resp, err := client.Do(req) |
||||
if err != nil { |
||||
warningFailed.Add(1) |
||||
return err |
||||
} |
||||
defer resp.Body.Close() |
||||
|
||||
if resp.StatusCode != http.StatusOK { |
||||
warningFailed.Add(1) |
||||
return errors.New(resp.Status) |
||||
} |
||||
|
||||
body, _ := io.ReadAll(resp.Body) |
||||
if s := strings.TrimSpace(string(body)); s != "ok" { |
||||
warningFailed.Add(1) |
||||
return errors.New("non-ok response returned from Slack") |
||||
} |
||||
warningGenerated.Add(1) |
||||
return nil |
||||
} |
||||
Loading…
Reference in new issue