diff --git a/etc/server.conf b/etc/server.conf index 70b27b18869e92991cd45f072042ee99e83e35da..6e96e65b91ce08a5ccadef553ae55361b6d603b8 100644 --- a/etc/server.conf +++ b/etc/server.conf @@ -64,6 +64,14 @@ Enable = false # complete redis key: ${ChannelPrefix} + ${Cluster} ChannelPrefix = "/alerts/" +[Alerting.GlobalCallback] +Enable = false +Url = "http://a.com/n9e/callback" +BasicAuthUser = "" +BasicAuthPass = "" +Timeout = "5s" +Headers = ["Content-Type", "application/json", "X-From", "N9E"] + [NoData] Metric = "target_up" # unit: second diff --git a/src/server/config/config.go b/src/server/config/config.go index 1951f650a0f35c9d595e20662353ffa99e254006..07b7a7e4ebcb92b471d01efd3600dbf59b7cc5bc 100644 --- a/src/server/config/config.go +++ b/src/server/config/config.go @@ -6,6 +6,7 @@ import ( "os" "strings" "sync" + "time" "github.com/gin-gonic/gin" "github.com/koding/multiconfig" @@ -76,9 +77,21 @@ func MustLoad(fpaths ...string) { } C.Heartbeat.Endpoint = fmt.Sprintf("%s:%d", C.Heartbeat.IP, C.HTTP.Port) - C.Alerting.RedisPub.ChannelKey = C.Alerting.RedisPub.ChannelPrefix + C.ClusterName + if C.Alerting.GlobalCallback.Enable { + if C.Alerting.GlobalCallback.Timeout == "" { + C.Alerting.GlobalCallback.TimeoutDuration = time.Second * 5 + } else { + dur, err := time.ParseDuration(C.Alerting.GlobalCallback.Timeout) + if err != nil { + fmt.Println("failed to parse Alerting.GlobalCallback.Timeout") + os.Exit(1) + } + C.Alerting.GlobalCallback.TimeoutDuration = dur + } + } + fmt.Println("heartbeat.ip:", C.Heartbeat.IP) fmt.Printf("heartbeat.interval: %dms\n", C.Heartbeat.Interval) }) @@ -115,6 +128,7 @@ type Alerting struct { NotifyConcurrency int TemplatesDir string RedisPub RedisPub + GlobalCallback GlobalCallback } type RedisPub struct { @@ -123,6 +137,16 @@ type RedisPub struct { ChannelKey string } +type GlobalCallback struct { + Enable bool + Url string + BasicAuthUser string + BasicAuthPass string + Timeout string + TimeoutDuration time.Duration + Headers []string +} + type NoData struct { Metric string Interval int64 diff --git a/src/server/engine/notify.go b/src/server/engine/notify.go index 5322eb5c091adfffa7c8aca71501db68e0235aa9..44d35271ff861b791e00f224fc8653e0e5492a2d 100644 --- a/src/server/engine/notify.go +++ b/src/server/engine/notify.go @@ -5,6 +5,8 @@ import ( "context" "encoding/json" "html/template" + "io/ioutil" + "net/http" "os/exec" "path" "strings" @@ -119,6 +121,10 @@ func notify(event *models.AlertCurEvent) { } } + if config.C.Alerting.GlobalCallback.Enable { + DoGlobalCallback(event) + } + // no notify.py? do nothing if config.C.Alerting.NotifyScriptPath == "" { return @@ -138,6 +144,55 @@ func notify(event *models.AlertCurEvent) { } } +func DoGlobalCallback(event *models.AlertCurEvent) { + conf := config.C.Alerting.GlobalCallback + if conf.Url == "" { + return + } + + bs, err := json.Marshal(event) + if err != nil { + return + } + + bf := bytes.NewBuffer(bs) + + req, err := http.NewRequest("POST", conf.Url, bf) + if err != nil { + logger.Warning("DoGlobalCallback failed to new request", err) + return + } + + if conf.BasicAuthUser != "" && conf.BasicAuthPass != "" { + req.SetBasicAuth(conf.BasicAuthUser, conf.BasicAuthPass) + } + + if len(conf.Headers) > 0 && len(conf.Headers)%2 == 0 { + for i := 0; i < len(conf.Headers); i += 2 { + req.Header.Set(conf.Headers[i], conf.Headers[i+1]) + } + } + + client := http.Client{ + Timeout: conf.TimeoutDuration, + } + + var resp *http.Response + resp, err = client.Do(req) + if err != nil { + logger.Warning("DoGlobalCallback failed to call url", err) + return + } + + var body []byte + if resp.Body != nil { + defer resp.Body.Close() + body, err = ioutil.ReadAll(resp.Body) + } + + logger.Debugf("DoGlobalCallback done, url: %s, response code: %d, body: %s", conf.Url, resp.StatusCode, string(body)) +} + func handleSubscribes(event models.AlertCurEvent, subs []*models.AlertSubscribe) { for i := 0; i < len(subs); i++ { handleSubscribe(event, subs[i])