dingding.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package notifiers
  15. import (
  16. "encoding/json"
  17. "net/url"
  18. "yunion.io/x/jsonutils"
  19. "yunion.io/x/log"
  20. "yunion.io/x/pkg/errors"
  21. "yunion.io/x/pkg/utils"
  22. "yunion.io/x/onecloud/pkg/apis/monitor"
  23. "yunion.io/x/onecloud/pkg/httperrors"
  24. "yunion.io/x/onecloud/pkg/mcclient"
  25. "yunion.io/x/onecloud/pkg/monitor/alerting"
  26. "yunion.io/x/onecloud/pkg/monitor/alerting/notifiers/templates"
  27. )
  28. const (
  29. defaultDingdingMsgType = DingdingMsgTypeMarkdown
  30. DingdingMsgTypeLink = "link"
  31. DingdingMsgTypeMarkdown = "markdown"
  32. DingdingMsgTypeActionCard = "actionCard"
  33. )
  34. func init() {
  35. alerting.RegisterNotifier(&alerting.NotifierPlugin{
  36. Type: monitor.AlertNotificationTypeDingding,
  37. Factory: newDingdingNotifier,
  38. ValidateCreateData: func(cred mcclient.IIdentityProvider, input monitor.NotificationCreateInput) (monitor.NotificationCreateInput, error) {
  39. settings := new(monitor.NotificationSettingDingding)
  40. if err := input.Settings.Unmarshal(settings); err != nil {
  41. return input, errors.Wrap(err, "unmarshal setting")
  42. }
  43. if settings.Url == "" {
  44. return input, httperrors.NewInputParameterError("url is empty")
  45. }
  46. if _, err := url.Parse(settings.Url); err != nil {
  47. return input, httperrors.NewInputParameterError("invalid url: %v", err)
  48. }
  49. if settings.MessageType == "" {
  50. settings.MessageType = defaultDingdingMsgType
  51. }
  52. if !utils.IsInStringArray(settings.MessageType, []string{
  53. DingdingMsgTypeMarkdown,
  54. DingdingMsgTypeLink,
  55. DingdingMsgTypeActionCard,
  56. }) {
  57. return input, httperrors.NewInputParameterError("unsupport type: %s", settings.MessageType)
  58. }
  59. input.Settings = jsonutils.Marshal(settings)
  60. return input, nil
  61. },
  62. })
  63. }
  64. type DingDingNotifier struct {
  65. NotifierBase
  66. MsgType string
  67. Url string
  68. }
  69. func newDingdingNotifier(config alerting.NotificationConfig) (alerting.Notifier, error) {
  70. settings := new(monitor.NotificationSettingDingding)
  71. if err := config.Settings.Unmarshal(settings); err != nil {
  72. return nil, errors.Wrap(err, "unmarshal setting")
  73. }
  74. return &DingDingNotifier{
  75. NotifierBase: NewNotifierBase(config),
  76. Url: settings.Url,
  77. MsgType: settings.MessageType,
  78. }, nil
  79. }
  80. func (dd *DingDingNotifier) Notify(evalCtx *alerting.EvalContext, d jsonutils.JSONObject) error {
  81. log.Infof("Sending alert notification to dingding")
  82. errs := []error{}
  83. if len(evalCtx.EvalMatches) > 0 {
  84. if err := dd.notify(evalCtx, evalCtx.EvalMatches, false, d); err != nil {
  85. errs = append(errs, errors.Wrap(err, "notify alerting matches"))
  86. }
  87. }
  88. if evalCtx.HasRecoveredMatches() && !evalCtx.Rule.DisableNotifyRecovery {
  89. if err := dd.notify(evalCtx, evalCtx.GetRecoveredMatches(), true, d); err != nil {
  90. errs = append(errs, errors.Wrap(err, "notify recovered matches"))
  91. }
  92. }
  93. return errors.NewAggregate(errs)
  94. }
  95. func (dd *DingDingNotifier) notify(ctx *alerting.EvalContext, matches []*monitor.EvalMatch, isRecoverd bool, _ jsonutils.JSONObject) error {
  96. // msgUrl, err := ctx.GetRuleURL()
  97. body, err := dd.genBody(ctx, matches, isRecoverd)
  98. if err != nil {
  99. return err
  100. }
  101. input := &monitor.SendWebhookSync{
  102. Url: dd.Url,
  103. Body: string(body),
  104. }
  105. return SendWebRequestSync(ctx.Ctx, input)
  106. }
  107. func (dd *DingDingNotifier) genBody(ctx *alerting.EvalContext, matches []*monitor.EvalMatch, isRecoverd bool) ([]byte, error) {
  108. q := url.Values{
  109. "pc_slide": {"false"},
  110. // "url": {messageURL},
  111. }
  112. // Use special link to auto open the message url outside of Dingding
  113. // Refer: https://open-doc.dingtalk.com/docs/doc.htm?treeId=385&articleId=104972&docType=1#s9
  114. messageURL := "dingtalk://dingtalkclient/page/link?" + q.Encode()
  115. log.Infof("messageUrl: %s", messageURL)
  116. config := GetNotifyTemplateConfig(ctx, isRecoverd, matches)
  117. contentConfig := templates.NewTemplateConfig(config)
  118. content, err := contentConfig.GenerateMarkdown()
  119. if err != nil {
  120. return nil, errors.Wrap(err, "build content")
  121. }
  122. var bodyMsg map[string]interface{}
  123. switch dd.MsgType {
  124. case DingdingMsgTypeMarkdown:
  125. bodyMsg = map[string]interface{}{
  126. "msgtype": DingdingMsgTypeMarkdown,
  127. DingdingMsgTypeMarkdown: map[string]string{
  128. "title": config.Title,
  129. "text": content,
  130. },
  131. }
  132. case DingdingMsgTypeActionCard:
  133. bodyMsg = map[string]interface{}{
  134. "msgtype": DingdingMsgTypeActionCard,
  135. DingdingMsgTypeActionCard: map[string]string{
  136. "text": content,
  137. "title": config.Title,
  138. // "singleTitle": "More",
  139. // "singleURL": messageURL,
  140. },
  141. }
  142. case DingdingMsgTypeLink:
  143. bodyMsg = map[string]interface{}{
  144. "msgtype": DingdingMsgTypeLink,
  145. "link": map[string]string{
  146. "text": content,
  147. "title": config.Title,
  148. // "messageUrl": messageURL,
  149. },
  150. }
  151. }
  152. return json.Marshal(bodyMsg)
  153. }