Skip to content

Commit e43ba87

Browse files
committed
Refactoring
Signed-off-by: Markus Blaschke <[email protected]>
1 parent d73bc11 commit e43ba87

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+5377
-13563
lines changed

.dockerignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/vendor/
2+
/alertmanager2es
3+
*.exe

.editorconfig

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# EditorConfig is awesome: http://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
charset = utf-8
6+
trim_trailing_whitespace = true
7+
8+
[*]
9+
end_of_line = lf
10+
insert_final_newline = true
11+
indent_style = space
12+
indent_size = 4
13+
14+
[Makefile]
15+
indent_style = tab
16+
17+
[*.yml]
18+
indent_size = 2
19+
20+
[*.yaml]
21+
indent_size = 2
22+
23+
[*.conf]
24+
indent_size = 2
25+
26+
[*.go]
27+
indent_style = tab
28+
indent_size = 4

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
alertmanager2es
1+
/vendor/
2+
/alertmanager2es
3+
*.exe

.travis.yml

Lines changed: 0 additions & 9 deletions
This file was deleted.

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Contributions are welcomed.
66

77
To download, run:
88

9-
go get -u github.com/cloudflare/alertmanager2es
9+
go get -u github.com/webdevops/alertmanager2es
1010

1111
To run the tests:
1212

Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
FROM golang:1.14 as build
2+
3+
WORKDIR /go/src/github.com/webdevops/alertmanager2es
4+
5+
# Get deps (cached)
6+
COPY ./go.mod /go/src/github.com/webdevops/alertmanager2es
7+
COPY ./go.sum /go/src/github.com/webdevops/alertmanager2es
8+
RUN go mod download
9+
10+
# Compile
11+
COPY ./ /go/src/github.com/webdevops/alertmanager2es
12+
RUN make lint
13+
RUN make build
14+
RUN ./alertmanager2es --help
15+
16+
#############################################
17+
# FINAL IMAGE
18+
#############################################
19+
FROM gcr.io/distroless/static
20+
COPY --from=build /go/src/github.com/webdevops/alertmanager2es/alertmanager2es /
21+
USER 1000
22+
ENTRYPOINT ["/alertmanager2es"]

Makefile

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,39 @@
1-
.PHONY: build test
1+
.PHONY: all build clean image check vendor dependencies
22

3-
build: test
4-
go build -ldflags "-X main.revision=$(shell git describe --tags --always --dirty=-dev)"
3+
NAME := alertmanager2es
4+
GIT_TAG := $(shell git describe --dirty --tags --always)
5+
GIT_COMMIT := $(shell git rev-parse --short HEAD)
6+
LDFLAGS := -X "main.gitTag=$(GIT_TAG)" -X "main.gitCommit=$(GIT_COMMIT)" -extldflags "-static"
7+
8+
PKGS := $(shell go list ./... | grep -v -E '/vendor/|/test')
9+
FIRST_GOPATH := $(firstword $(subst :, ,$(shell go env GOPATH)))
10+
GOLANGCI_LINT_BIN := $(FIRST_GOPATH)/bin/golangci-lint
11+
12+
13+
all: build
14+
15+
clean:
16+
git clean -Xfd .
17+
18+
build:
19+
CGO_ENABLED=0 go build -a -ldflags '$(LDFLAGS)' -o $(NAME) .
20+
21+
vendor:
22+
go mod tidy
23+
go mod vendor
24+
go mod verify
25+
26+
image: build
27+
docker build -t $(NAME):$(TAG) .
28+
29+
.PHONY: lint
30+
lint: $(GOLANGCI_LINT_BIN)
31+
# megacheck fails to respect build flags, causing compilation failure during linting.
32+
# instead, use the unused, gosimple, and staticcheck linters directly
33+
$(GOLANGCI_LINT_BIN) run -D megacheck -E unused,gosimple,staticcheck --timeout=10m
34+
35+
dependencies: $(GOLANGCI_LINT_BIN)
36+
37+
$(GOLANGCI_LINT_BIN):
38+
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(FIRST_GOPATH)/bin v1.23.8
539

6-
test:
7-
go test $(go list ./... | grep -v /vendor/)

README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# alertmanager2es
22

3+
[![license](https://img.shields.io/github/license/webdevops/alertmanager2es.svg)](https://github.com/webdevops/alertmanager2es/blob/master/LICENSE)
4+
[![Docker](https://img.shields.io/docker/cloud/automated/webdevops/alertmanager2es)](https://hub.docker.com/r/webdevops/alertmanager2es/)
5+
[![Docker Build Status](https://img.shields.io/docker/cloud/build/webdevops/alertmanager2es)](https://hub.docker.com/r/webdevops/alertmanager2es/)
6+
``
7+
This is a forked version of [cloudflare's alertmanager2es](https://github.com/cloudflare/alertmanager2es) with
8+
new golang layout and uses the official ElasticSearch client. It also supports Authentication.
9+
310
alertmanager2es receives [HTTP webhook][] notifications from [AlertManager][]
411
and inserts them into an [Elasticsearch][] index for searching and analysis. It
512
runs as a daemon.
@@ -53,7 +60,7 @@ To use alertmanager2es, you'll need:
5360
To build alertmanager2es, you'll need:
5461

5562
- [Make][]
56-
- [Go][] 1.7 or above
63+
- [Go][] 1.14 or above
5764
- a working [GOPATH][]
5865

5966
[Make]: https://www.gnu.org/software/make/
@@ -62,9 +69,10 @@ To build alertmanager2es, you'll need:
6269

6370
## Building
6471

65-
go get -u github.com/cloudflare/alertmanager2es
66-
cd $GOPATH/src/github.com/cloudflare/alertmanager2es
67-
make
72+
git clone github.com/webdevops/alertmanager2elasticsearch
73+
cd alertmanager2elasticsearch
74+
make vendor
75+
make build
6876

6977
## Configuration
7078

@@ -143,11 +151,7 @@ sending data:
143151
We rotate our index once a month, since there's not enough data to warrant
144152
daily rotation in our case. Therefore our index name looks like:
145153

146-
alertmanager-200601
147-
148-
We anchor the template name with `-2` to avoid inadvertently matching other
149-
indices, e.g. `alertmanager-foo-200601`. This of course assumes that you will
150-
no longer care to index your alerts in the year 3000.
154+
alertmanager-2020.06
151155

152156
## Failure modes
153157

VERSION

Lines changed: 0 additions & 1 deletion
This file was deleted.

alertmanager2es.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"errors"
8+
"fmt"
9+
elasticsearch "github.com/elastic/go-elasticsearch/v7"
10+
"github.com/elastic/go-elasticsearch/v7/esapi"
11+
"github.com/prometheus/client_golang/prometheus"
12+
"io/ioutil"
13+
"net/http"
14+
"strings"
15+
"time"
16+
)
17+
18+
const supportedWebhookVersion = "4"
19+
20+
type (
21+
AlertmanagerElasticsearchExporter struct {
22+
elasticSearchClient *elasticsearch.Client
23+
elasticsearchIndexName string
24+
elasticsearchBatchCount int
25+
elasticsearchRetryCount int
26+
elasticsearchRetryDelay time.Duration
27+
28+
prometheus struct {
29+
alertsReceived *prometheus.CounterVec
30+
alertsInvalid *prometheus.CounterVec
31+
alertsSuccessful *prometheus.CounterVec
32+
}
33+
}
34+
35+
AlertmanagerEntry struct {
36+
Alerts []struct {
37+
Annotations map[string]string `json:"annotations"`
38+
EndsAt time.Time `json:"endsAt"`
39+
GeneratorURL string `json:"generatorURL"`
40+
Labels map[string]string `json:"labels"`
41+
StartsAt time.Time `json:"startsAt"`
42+
Status string `json:"status"`
43+
} `json:"alerts"`
44+
CommonAnnotations map[string]string `json:"commonAnnotations"`
45+
CommonLabels map[string]string `json:"commonLabels"`
46+
ExternalURL string `json:"externalURL"`
47+
GroupLabels map[string]string `json:"groupLabels"`
48+
Receiver string `json:"receiver"`
49+
Status string `json:"status"`
50+
Version string `json:"version"`
51+
GroupKey string `json:"groupKey"`
52+
53+
// Timestamp records when the alert notification was received
54+
Timestamp string `json:"@timestamp"`
55+
}
56+
)
57+
58+
func (e *AlertmanagerElasticsearchExporter) Init() {
59+
e.prometheus.alertsReceived = prometheus.NewCounterVec(
60+
prometheus.CounterOpts{
61+
Name: "alertmanager2es_alerts_received",
62+
Help: "alertmanager2es received alerts",
63+
},
64+
[]string{},
65+
)
66+
prometheus.MustRegister(e.prometheus.alertsReceived)
67+
68+
e.prometheus.alertsInvalid = prometheus.NewCounterVec(
69+
prometheus.CounterOpts{
70+
Name: "alertmanager2es_alerts_invalid",
71+
Help: "alertmanager2es invalid alerts",
72+
},
73+
[]string{},
74+
)
75+
prometheus.MustRegister(e.prometheus.alertsInvalid)
76+
77+
e.prometheus.alertsSuccessful = prometheus.NewCounterVec(
78+
prometheus.CounterOpts{
79+
Name: "alertmanager2es_alerts_successful",
80+
Help: "alertmanager2es successful stored alerts",
81+
},
82+
[]string{},
83+
)
84+
prometheus.MustRegister(e.prometheus.alertsSuccessful)
85+
}
86+
87+
func (e *AlertmanagerElasticsearchExporter) ConnectElasticsearch(cfg elasticsearch.Config, indexName string) {
88+
var err error
89+
e.elasticSearchClient, err = elasticsearch.NewClient(cfg)
90+
if err != nil {
91+
panic(err)
92+
}
93+
94+
tries := 0
95+
for {
96+
_, err = e.elasticSearchClient.Info()
97+
if err != nil {
98+
tries++
99+
if tries >= 5 {
100+
panic(err)
101+
} else {
102+
daemonLogger.Info("Failed to connect to ES, retry...")
103+
time.Sleep(5 * time.Second)
104+
continue
105+
}
106+
}
107+
108+
break
109+
}
110+
111+
e.elasticsearchIndexName = indexName
112+
}
113+
114+
func (e *AlertmanagerElasticsearchExporter) buildIndexName(createTime time.Time) string {
115+
ret := e.elasticsearchIndexName
116+
117+
ret = strings.Replace(ret, "%y", createTime.Format("2006"), -1)
118+
ret = strings.Replace(ret, "%m", createTime.Format("01"), -1)
119+
ret = strings.Replace(ret, "%d", createTime.Format("02"), -1)
120+
121+
return ret
122+
}
123+
124+
func (e *AlertmanagerElasticsearchExporter) HttpHandler(w http.ResponseWriter, r *http.Request) {
125+
e.prometheus.alertsReceived.WithLabelValues().Inc()
126+
127+
if r.Body == nil {
128+
e.prometheus.alertsInvalid.WithLabelValues().Inc()
129+
err := errors.New("got empty request body")
130+
http.Error(w, err.Error(), http.StatusBadRequest)
131+
daemonLogger.Error(err)
132+
return
133+
}
134+
135+
b, err := ioutil.ReadAll(r.Body)
136+
if err != nil {
137+
e.prometheus.alertsInvalid.WithLabelValues().Inc()
138+
http.Error(w, err.Error(), http.StatusInternalServerError)
139+
daemonLogger.Error(err)
140+
return
141+
}
142+
defer r.Body.Close()
143+
144+
var msg AlertmanagerEntry
145+
err = json.Unmarshal(b, &msg)
146+
if err != nil {
147+
e.prometheus.alertsInvalid.WithLabelValues().Inc()
148+
http.Error(w, err.Error(), http.StatusBadRequest)
149+
daemonLogger.Error(err)
150+
return
151+
}
152+
153+
if msg.Version != supportedWebhookVersion {
154+
e.prometheus.alertsInvalid.WithLabelValues().Inc()
155+
err := fmt.Errorf("do not understand webhook version %q, only version %q is supported.", msg.Version, supportedWebhookVersion)
156+
http.Error(w, err.Error(), http.StatusBadRequest)
157+
daemonLogger.Error(err)
158+
return
159+
}
160+
161+
now := time.Now()
162+
msg.Timestamp = now.Format(time.RFC3339)
163+
164+
incidentJson, _ := json.Marshal(msg)
165+
166+
req := esapi.IndexRequest{
167+
Index: e.buildIndexName(now),
168+
Body: bytes.NewReader(incidentJson),
169+
}
170+
res, err := req.Do(context.Background(), e.elasticSearchClient)
171+
if err != nil {
172+
e.prometheus.alertsInvalid.WithLabelValues().Inc()
173+
err := fmt.Errorf("unable to insert document in elasticsearch")
174+
http.Error(w, err.Error(), http.StatusBadRequest)
175+
daemonLogger.Error(err)
176+
return
177+
}
178+
defer res.Body.Close()
179+
180+
daemonLogger.Verbosef("received and stored alert: %v", msg.CommonLabels)
181+
e.prometheus.alertsSuccessful.WithLabelValues().Inc()
182+
}

0 commit comments

Comments
 (0)