Skip to content

Commit

Permalink
fix wechat issue
Browse files Browse the repository at this point in the history
  • Loading branch information
songjiayang committed Feb 8, 2018
1 parent a43a513 commit 48b8b8e
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 89 deletions.
7 changes: 5 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,24 +243,27 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
}
}
for _, wcc := range rcv.WechatConfigs {
wcc.APIURL = c.Global.WeChatAPIURL
if wcc.APIURL == "" {
if c.Global.WeChatAPIURL == "" {
return fmt.Errorf("no global Wechat URL set")
}
wcc.APIURL = c.Global.WeChatAPIURL
}
wcc.APISecret = c.Global.WeChatAPISecret

if wcc.APISecret == "" {
if c.Global.WeChatAPISecret == "" {
return fmt.Errorf("no global Wechat ApiSecret set")
}
wcc.APISecret = c.Global.WeChatAPISecret
}

if wcc.CorpID == "" {
if c.Global.WeChatAPICorpID == "" {
return fmt.Errorf("no global Wechat CorpID set")
}
wcc.CorpID = c.Global.WeChatAPICorpID
}

if !strings.HasSuffix(wcc.APIURL, "/") {
wcc.APIURL += "/"
}
Expand Down
142 changes: 75 additions & 67 deletions notify/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -797,33 +797,30 @@ type Wechat struct {
conf *config.WechatConfig
tmpl *template.Template
logger log.Logger

accessToken string
accessTokenAt time.Time
}

// Wechat AccessToken with corpid and corpsecret.
type WechatToken struct {
AccessToken string `json:"access_token"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `json:"-"`
}

type weChatMessage struct {
Content string `json:"content"`
}
type weChatCreateMessage struct {
Text weChatMessage `yaml:"text,omitempty" json:"text,omitempty"`
ToUser string `yaml:"touser,omitempty" json:"touser,omitempty"`
ToParty string `yaml:"toparty,omitempty" json:"toparty,omitempty"`
Totag string `yaml:"totag,omitempty" json:"totag,omitempty"`
AgentID string `yaml:"agentid,omitempty" json:"agentid,omitempty"`
Safe string `yaml:"safe,omitempty" json:"safe,omitempty"`
Type string `yaml:"msgtype,omitempty" json:"msgtype,omitempty"`
Text weChatMessageContent `yaml:"text,omitempty" json:"text,omitempty"`
ToUser string `yaml:"touser,omitempty" json:"touser,omitempty"`
ToParty string `yaml:"toparty,omitempty" json:"toparty,omitempty"`
Totag string `yaml:"totag,omitempty" json:"totag,omitempty"`
AgentID string `yaml:"agentid,omitempty" json:"agentid,omitempty"`
Safe string `yaml:"safe,omitempty" json:"safe,omitempty"`
Type string `yaml:"msgtype,omitempty" json:"msgtype,omitempty"`
}

type weChatCloseMessage struct {
Text weChatMessage `yaml:"text,omitempty" json:"text,omitempty"`
ToUser string `yaml:"touser,omitempty" json:"touser,omitempty"`
ToParty string `yaml:"toparty,omitempty" json:"toparty,omitempty"`
Totag string `yaml:"totag,omitempty" json:"totag,omitempty"`
AgentID string `yaml:"agentid,omitempty" json:"agentid,omitempty"`
Safe string `yaml:"safe,omitempty" json:"safe,omitempty"`
Type string `yaml:"msgtype,omitempty" json:"msgtype,omitempty"`
type weChatMessageContent struct {
Content string `json:"content"`
}

type weChatErrorResponse struct {
Expand All @@ -842,75 +839,86 @@ func (n *Wechat) Notify(ctx context.Context, as ...*types.Alert) (bool, error) {
if !ok {
return false, fmt.Errorf("group key missing")
}
data := n.tmpl.Data(receiverName(ctx, n.logger), groupLabels(ctx, n.logger), as...)

level.Debug(n.logger).Log("msg", "Notifying Wechat", "incident", key)
data := n.tmpl.Data(receiverName(ctx, n.logger), groupLabels(ctx, n.logger), as...)

var err error
tmpl := tmplText(n.tmpl, data, &err)

var (
msg interface{}
apiURL string
apiMsg = weChatMessage{
Content: tmpl(n.conf.Message),
}
alerts = types.Alerts(as...)
)
parameters := url.Values{}
parameters.Add("corpsecret", tmpl(string(n.conf.APISecret)))
parameters.Add("corpid", tmpl(string(n.conf.CorpID)))
apiURL = n.conf.APIURL + "gettoken"
u, err := url.Parse(apiURL)
if err != nil {
return false, err
}
u.RawQuery = parameters.Encode()
level.Debug(n.logger).Log("msg", "Sending Wechat message", "incident", key, "url", u.String())
resp, err := ctxhttp.Get(ctx, http.DefaultClient, u.String())
if err != nil {
return true, err
}
defer resp.Body.Close()
var wechatToken WechatToken
if err := json.NewDecoder(resp.Body).Decode(&wechatToken); err != nil {
return false, err
}
postMessageURL := n.conf.APIURL + "message/send?access_token=" + wechatToken.AccessToken
switch alerts.Status() {
case model.AlertResolved:
msg = &weChatCloseMessage{Text: apiMsg,
ToUser: tmpl(n.conf.ToUser),
ToParty: tmpl(n.conf.ToParty),
Totag: tmpl(n.conf.ToTag),
AgentID: tmpl(n.conf.AgentID),
Type: "text",
Safe: "0"}
default:
msg = &weChatCreateMessage{
Text: weChatMessage{
Content: tmpl(n.conf.Message),
},
ToUser: tmpl(n.conf.ToUser),
ToParty: tmpl(n.conf.ToParty),
Totag: tmpl(n.conf.ToTag),
AgentID: tmpl(n.conf.AgentID),
Type: "text",
Safe: "0",

var accessToken string
// Refresh AccessToken over 2 hours
if n.accessToken != "" && time.Now().Sub(n.accessTokenAt) < 110*time.Minute {
accessToken = n.accessToken
} else {
parameters := url.Values{}
parameters.Add("corpsecret", tmpl(string(n.conf.APISecret)))
parameters.Add("corpid", tmpl(string(n.conf.CorpID)))

apiURL := n.conf.APIURL + "gettoken"

u, err := url.Parse(apiURL)
if err != nil {
return false, err
}

u.RawQuery = parameters.Encode()

level.Debug(n.logger).Log("msg", "Sending Wechat message", "incident", key, "url", u.String())

resp, err := ctxhttp.Get(ctx, http.DefaultClient, u.String())
if err != nil {
return true, err
}
defer resp.Body.Close()

var wechatToken WechatToken
if err := json.NewDecoder(resp.Body).Decode(&wechatToken); err != nil {
return false, err
}

accessToken = wechatToken.AccessToken

// Cache accessToken
n.accessToken = accessToken
n.accessTokenAt = time.Now()
}

msg := &weChatMessage{
Text: weChatMessageContent{
Content: tmpl(n.conf.Message),
},
ToUser: n.conf.ToUser,
ToParty: n.conf.ToParty,
Totag: n.conf.ToTag,
AgentID: n.conf.AgentID,
Type: "text",
Safe: "0",
}

var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(msg); err != nil {
return false, err
}
resp, err = ctxhttp.Post(ctx, http.DefaultClient, postMessageURL, contentTypeJSON, &buf)

postMessageURL := n.conf.APIURL + "message/send?access_token=" + accessToken

resp, err := ctxhttp.Post(ctx, http.DefaultClient, postMessageURL, contentTypeJSON, &buf)
if err != nil {
return true, err
}

defer resp.Body.Close()

body, _ := ioutil.ReadAll(resp.Body)
level.Debug(n.logger).Log("msg", "response: "+string(body), "incident", key)
defer resp.Body.Close()

return n.retry(resp.StatusCode)
}

func (n *Wechat) retry(statusCode int) (bool, error) {
// https://work.weixin.qq.com/api/doc#10649
if statusCode/100 == 5 || statusCode == 429 {
Expand Down
40 changes: 20 additions & 20 deletions notify/impl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import (
"github.com/stretchr/testify/require"
"golang.org/x/net/context"

"github.com/prometheus/common/model"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/template"
"net/url"
"github.com/prometheus/alertmanager/types"
"github.com/prometheus/common/model"
"io/ioutil"
"net/url"
)

func TestWebhookRetry(t *testing.T) {
Expand Down Expand Up @@ -200,7 +200,7 @@ func createTmpl(t *testing.T) *template.Template {
return tmpl
}

func readBody(t *testing.T, r *http.Request) string {
func readBody(t *testing.T, r *http.Request) string {
body, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
return string(body)
Expand All @@ -216,7 +216,7 @@ func TestOpsGenie(t *testing.T) {
Message: `{{ .CommonLabels.Message }}`,
Description: `{{ .CommonLabels.Description }}`,
Source: `{{ .CommonLabels.Source }}`,
Teams: `{{ .CommonLabels.Teams }}`,
Teams: `{{ .CommonLabels.Teams }}`,
Tags: `{{ .CommonLabels.Tags }}`,
Note: `{{ .CommonLabels.Note }}`,
Priority: `{{ .CommonLabels.Priority }}`,
Expand All @@ -231,11 +231,11 @@ func TestOpsGenie(t *testing.T) {
expectedUrl, _ := url.Parse("https://opsgenie/apiv2/alerts")

// Empty alert.
alert1:= &types.Alert{
Alert: model.Alert{
StartsAt: time.Now(),
EndsAt: time.Now().Add(time.Hour),
},
alert1 := &types.Alert{
Alert: model.Alert{
StartsAt: time.Now(),
EndsAt: time.Now().Add(time.Hour),
},
}
expectedBody := `{"alias":"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b","message":"","details":{},"source":""}
`
Expand All @@ -247,23 +247,23 @@ func TestOpsGenie(t *testing.T) {
require.Equal(t, expectedBody, readBody(t, req))

// Fully defined alert.
alert2:= &types.Alert{
alert2 := &types.Alert{
Alert: model.Alert{
Labels: model.LabelSet{
"Message": "message",
"Message": "message",
"Description": "description",
"Source": "http://prometheus",
"Teams": "TeamA,TeamB,",
"Tags": "tag1,tag2",
"Note": "this is a note",
"Priotity": "P1",
},
"Source": "http://prometheus",
"Teams": "TeamA,TeamB,",
"Tags": "tag1,tag2",
"Note": "this is a note",
"Priotity": "P1",
},
StartsAt: time.Now(),
EndsAt: time.Now().Add(time.Hour),
EndsAt: time.Now().Add(time.Hour),
},
}
expectedBody = `{"alias":"6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b","message":"message","description":"description","details":{},"source":"http://prometheus","teams":[{"name":"TeamA"},{"name":"TeamB"}],"tags":["tag1","tag2"],"note":"this is a note"}
`
req, retry, err = notifier.createRequest(ctx, alert2)
require.Equal(t, expectedBody, readBody(t, req))
}
}

0 comments on commit 48b8b8e

Please sign in to comment.