diff --git a/cli/Dockerfile b/cli/Dockerfile new file mode 100644 index 0000000..7fa15ca --- /dev/null +++ b/cli/Dockerfile @@ -0,0 +1,12 @@ +# build stage +FROM golang:alpine AS build-env +RUN set -eux; \ + apk add --no-cache --virtual .build-deps \ + git gcc libc-dev; +ENV GO111MODULE on +WORKDIR /go/main +ADD go.mod go.sum ./ +RUN go mod download +# WORKDIR /go/src/status-monitor +ADD main.go ./ +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -a -installsuffix cgo -o main main.go diff --git a/cli/Makefile b/cli/Makefile new file mode 100644 index 0000000..8d9ae53 --- /dev/null +++ b/cli/Makefile @@ -0,0 +1,3 @@ +build: + docker build . + diff --git a/cli/go.mod b/cli/go.mod new file mode 100644 index 0000000..759b6a6 --- /dev/null +++ b/cli/go.mod @@ -0,0 +1,12 @@ +module main + +go 1.13 + +require ( + github.com/castawaylabs/cachet-monitor v1.1.0 // indirect + github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 // indirect + github.com/macchiang/cachet-monitor v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/sirupsen/logrus v1.4.2 // indirect + gopkg.in/yaml.v2 v2.2.7 // indirect +) diff --git a/cli/go.sum b/cli/go.sum new file mode 100644 index 0000000..32482f1 --- /dev/null +++ b/cli/go.sum @@ -0,0 +1,20 @@ +github.com/castawaylabs/cachet-monitor v1.1.0 h1:T8NshhwHjEIbdlWkeRBuYdnU2ktpBnZpwgh6nxrp62w= +github.com/castawaylabs/cachet-monitor v1.1.0/go.mod h1:FBK1+4+fLd80wd/+U4Zy8YshPlpgWEvup79AoapOZ2E= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/macchiang/cachet-monitor v1.1.0 h1:um5ymKBF11Dau8yKCAg82HS6F/og0tODiIOLOdwnnzc= +github.com/macchiang/cachet-monitor v1.1.0/go.mod h1:cNiyqtsa4pEzPn0v2tlf6oejVg4mfUts2qNaxFnCDeE= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/cli/main.go b/cli/main.go index 5a29b72..164e748 100644 --- a/cli/main.go +++ b/cli/main.go @@ -12,7 +12,7 @@ import ( "sync" "github.com/sirupsen/logrus" - cachet "github.com/castawaylabs/cachet-monitor" + cachet "github.com/macchiang/cachet-monitor" docopt "github.com/docopt/docopt-go" "github.com/mitchellh/mapstructure" "gopkg.in/yaml.v2" @@ -39,7 +39,7 @@ Options: --version Show version --immediate Tick immediately (by default waits for first defined interval) --restarted Get open incidents before start monitoring (if monitor died or restarted) - + Environment varaibles: CACHET_API override API url from configuration CACHET_TOKEN override API token from configuration diff --git a/config.go b/config.go index f4ab481..7e8e731 100644 --- a/config.go +++ b/config.go @@ -12,6 +12,7 @@ import ( type CachetMonitor struct { SystemName string `json:"system_name" yaml:"system_name"` DateFormat string `json:"date_format" yaml:"date_format"` + SlackWebhook string `json:"slack_webhook" yaml:"slack_webhook"` API CachetAPI `json:"api"` RawMonitors []map[string]interface{} `json:"monitors" yaml:"monitors"` diff --git a/example.config.json b/example.config.json index f74c46e..6ec4bbf 100644 --- a/example.config.json +++ b/example.config.json @@ -5,6 +5,7 @@ "insecure": false }, "date_format": "02/01/2006 15:04:05 MST", + "slack_webhook": "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX", "monitors": [ { "name": "google", @@ -56,4 +57,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/example.config.yml b/example.config.yml index 8cd05f9..e299631 100644 --- a/example.config.yml +++ b/example.config.yml @@ -6,6 +6,7 @@ api: insecure: false # https://golang.org/src/time/format.go#L57 date_format: 02/01/2006 15:04:05 MST +slack_webhook: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX monitors: # http monitor example - name: google @@ -15,7 +16,7 @@ monitors: strict: true # HTTP method method: POST - + # set to update component (either component_id or metric_id are required) component_id: 1 # set to post lag to cachet metric (graph) @@ -28,7 +29,7 @@ monitors: message: "{{ .Monitor.Name }} check **failed** (server time: {{ .now }})\n\n{{ .FailReason }}" fixed: subject: "I HAVE BEEN FIXED" - + # seconds between checks interval: 1 # seconds for timeout @@ -62,4 +63,4 @@ monitors: - regex: [1-9] alt[1-9].aspmx.l.google.com. - exact: 10 aspmx2.googlemail.com. - exact: 1 aspmx.l.google.com. - - exact: 10 aspmx3.googlemail.com. \ No newline at end of file + - exact: 10 aspmx3.googlemail.com. diff --git a/incident.go b/incident.go index 45336d3..21e6eeb 100644 --- a/incident.go +++ b/incident.go @@ -93,7 +93,10 @@ func (incident *Incident) Send(cfg *CachetMonitor) error { if resp.StatusCode != 200 { return fmt.Errorf("Could not create/update incident") } - + // send slack message + if cfg.SlackWebhook !="" { + sendSlack(cfg) + } return nil } @@ -136,3 +139,30 @@ func (incident *Incident) SetWatching() { func (incident *Incident) SetFixed() { incident.Status = 4 } + +// Send slack message +func (incident *Incident) sendSlack(cfg *CachetMonitor) { + color:="#bf1932" //red + if incident.ComponentStatus == 1 { + + color="#36a64f" //green + } + slack := Slack{ + WebhookUrl: cfg.SlackWebhook + Attachments: []Attachments{ + Attachments{ + Fallback: incident.Name, + Color: color, + Title: incident.Name, + TitleLink: "https://status.easyship.com", + Text: incident.Message, + Footer: "Cachet Monitor", + FooterIcon: "https://i.imgur.com/spck1w6.png", + Ts: time.Now().Unix(), + }, + }} + err := slack.SendSlackNotification() + if err != nil { + fmt.Errorf(err) + } +} diff --git a/slack.go b/slack.go new file mode 100644 index 0000000..2faef21 --- /dev/null +++ b/slack.go @@ -0,0 +1,80 @@ +package cachet + +import ( + "bytes" + "encoding/json" + "errors" + "log" + "net/http" + "time" +) + +type Slack struct { + WebhookUrl string + Attachments []Attachments `json:"attachments"` +} +type Fields struct { + Title string `json:"title"` + Value string `json:"value"` + Short bool `json:"short"` +} +type Attachments struct { + Fallback string `json:"fallback"` + Color string `json:"color"` + Pretext string `json:"pretext"` + Title string `json:"title"` + TitleLink string `json:"title_link"` + Text string `json:"text"` + Fields []Fields `json:"fields"` + ThumbURL string `json:"thumb_url"` + Footer string `json:"footer"` + FooterIcon string `json:"footer_icon"` + Ts int64 `json:"ts"` +} + +func test() { + slack := Slack{ + Attachments: []Attachments{ + Attachments{ + Fallback: "Required plain-text summary of the attachment.", + Color: "#36a64f", + Title: "Slack API Documentation", + TitleLink: "https://status.easyship.com", + Text: "Optional text that appears within the attachment", + Footer: "Cachet Monitor", + FooterIcon: "https://i.imgur.com/spck1w6.png", + Ts: time.Now().Unix(), + }, + }} + slack.WebhookUrl = "https://hooks.slack.com/services/0000000/00000000/xxxxxxxxxxxxxxxxxxx" + err := slack.SendSlackNotification() + if err != nil { + log.Fatal(err) + } +} + +// SendSlackNotification will post to an 'Incoming Webook' url setup in Slack Apps. It accepts +// some text and the slack channel is saved within Slack. +func (slack *Slack) SendSlackNotification() error { + + slackBody, _ := json.Marshal(slack) + req, err := http.NewRequest(http.MethodPost, slack.WebhookUrl, bytes.NewBuffer(slackBody)) + if err != nil { + return err + } + + req.Header.Add("Content-Type", "application/json") + + client := &http.Client{Timeout: 10 * time.Second} + resp, err := client.Do(req) + if err != nil { + return err + } + + buf := new(bytes.Buffer) + buf.ReadFrom(resp.Body) + if buf.String() != "ok" { + return errors.New("Non-ok response returned from Slack") + } + return nil +}