From d6d1013e0a869f1e8ca590f502792f25aceb3a5e Mon Sep 17 00:00:00 2001 From: liugq Date: Wed, 26 Jul 2023 17:54:42 +0800 Subject: [PATCH] add test api to send channel message --- plugin/api/alerting/api.go | 1 + plugin/api/alerting/channel.go | 58 ++++++++++++---- plugin/api/email/server.go | 21 ++---- service/alerting/common/helper.go | 87 ++++++++++++++++++++++++ service/alerting/elasticsearch/engine.go | 67 ++---------------- service/alerting/elasticsearch/helper.go | 24 ------- 6 files changed, 141 insertions(+), 117 deletions(-) create mode 100644 service/alerting/common/helper.go delete mode 100644 service/alerting/elasticsearch/helper.go diff --git a/plugin/api/alerting/api.go b/plugin/api/alerting/api.go index 6ce66dea..995ccd00 100644 --- a/plugin/api/alerting/api.go +++ b/plugin/api/alerting/api.go @@ -33,6 +33,7 @@ func (alert *AlertAPI) Init() { api.HandleAPIMethod(api.DELETE, "/alerting/channel", alert.RequirePermission(alert.deleteChannel, enum.PermissionAlertChannelWrite)) api.HandleAPIMethod(api.PUT, "/alerting/channel/:channel_id", alert.RequirePermission(alert.updateChannel, enum.PermissionAlertChannelWrite)) api.HandleAPIMethod(api.GET, "/alerting/channel/_search", alert.RequirePermission(alert.searchChannel, enum.PermissionAlertChannelRead)) + api.HandleAPIMethod(api.POST, "/alerting/channel/test", alert.RequirePermission(alert.testChannel, enum.PermissionAlertChannelWrite)) api.HandleAPIMethod(api.GET, "/alerting/alert/_search", alert.RequirePermission(alert.searchAlert, enum.PermissionAlertHistoryRead)) api.HandleAPIMethod(api.GET, "/alerting/alert/:alert_id", alert.RequirePermission(alert.getAlert, enum.PermissionAlertHistoryRead)) diff --git a/plugin/api/alerting/channel.go b/plugin/api/alerting/channel.go index a7a3dc6d..792c44c2 100644 --- a/plugin/api/alerting/channel.go +++ b/plugin/api/alerting/channel.go @@ -6,9 +6,11 @@ package alerting import ( "fmt" + "infini.sh/console/service/alerting/common" "net/http" "strconv" "strings" + "time" log "github.com/cihub/seelog" "infini.sh/console/model/alerting" @@ -33,10 +35,7 @@ func (h *AlertAPI) createChannel(w http.ResponseWriter, req *http.Request, ps ht return } - h.WriteJSON(w, util.MapStr{ - "_id": obj.ID, - "result": "created", - }, 200) + h.WriteCreatedOKJSON(w, obj.ID) } @@ -60,11 +59,7 @@ func (h *AlertAPI) getChannel(w http.ResponseWriter, req *http.Request, ps httpr return } - h.WriteJSON(w, util.MapStr{ - "found": true, - "_id": id, - "_source": obj, - }, 200) + h.WriteGetOKJSON(w, id, obj) } func (h *AlertAPI) updateChannel(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { @@ -101,10 +96,7 @@ func (h *AlertAPI) updateChannel(w http.ResponseWriter, req *http.Request, ps ht return } - h.WriteJSON(w, util.MapStr{ - "_id": obj.ID, - "result": "updated", - }, 200) + h.WriteUpdatedOKJSON(w, id) } func (h *AlertAPI) deleteChannel(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { @@ -179,3 +171,43 @@ func (h *AlertAPI) searchChannel(w http.ResponseWriter, req *http.Request, ps ht } h.Write(w, res.Raw) } + +func (h *AlertAPI) testChannel(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { + obj := alerting.Channel{} + err := h.DecodeJSON(req, &obj) + if err != nil { + h.WriteError(w, err.Error(), http.StatusInternalServerError) + log.Error(err) + return + } + ctx := map[string]interface{}{ + "title": "test title", + "message": "test message", + "rule_id": util.GetUUID(), + "rule_name": "test rule", + "resource_id": util.GetUUID(), + "resource_name": "test resource", + "event_id": util.GetUUID(), + "timestamp": time.Now().UnixMilli(), + "first_group_value": "first group value", + "first_threshold": "90", + "priority": "critical", + "results": []util.MapStr{ + {"threshold": "90", + "priority": "critical", + "group_values": []string{"first group value", "second group value"}, + "issue_timestamp": time.Now().UnixMilli()-500, + "result_value": 90, + "relation_values": util.MapStr{"a": 100, "b": 90}, + }, + }, + + } + _, err, _ = common.PerformChannel(&obj, ctx) + if err != nil { + h.WriteError(w, err.Error(), http.StatusInternalServerError) + log.Error(err) + return + } + h.WriteAckOKJSON(w) +} \ No newline at end of file diff --git a/plugin/api/email/server.go b/plugin/api/email/server.go index 4ac585d4..1797a312 100644 --- a/plugin/api/email/server.go +++ b/plugin/api/email/server.go @@ -80,10 +80,7 @@ func (h *EmailAPI) createEmailServer(w http.ResponseWriter, req *http.Request, p } } - h.WriteJSON(w, util.MapStr{ - "_id": obj.ID, - "result": "created", - }, 200) + h.WriteCreatedOKJSON(w, obj.ID) } @@ -107,11 +104,7 @@ func (h *EmailAPI) getEmailServer(w http.ResponseWriter, req *http.Request, ps h return } - h.WriteJSON(w, util.MapStr{ - "found": true, - "_id": id, - "_source": obj, - }, 200) + h.WriteGetOKJSON(w, id, obj) } func (h *EmailAPI) updateEmailServer(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { @@ -161,10 +154,7 @@ func (h *EmailAPI) updateEmailServer(w http.ResponseWriter, req *http.Request, p log.Error(err) } - h.WriteJSON(w, util.MapStr{ - "_id": obj.ID, - "result": "updated", - }, 200) + h.WriteUpdatedOKJSON(w, obj.ID) } func (h *EmailAPI) deleteEmailServer(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { @@ -202,10 +192,7 @@ func (h *EmailAPI) deleteEmailServer(w http.ResponseWriter, req *http.Request, p } } - h.WriteJSON(w, util.MapStr{ - "_id": obj.ID, - "result": "deleted", - }, 200) + h.WriteDeletedOKJSON(w, obj.ID) } func checkEmailServerReferenced(srv *model.EmailServer) error { diff --git a/service/alerting/common/helper.go b/service/alerting/common/helper.go new file mode 100644 index 00000000..fd8150b5 --- /dev/null +++ b/service/alerting/common/helper.go @@ -0,0 +1,87 @@ +/* Copyright © INFINI Ltd. All rights reserved. + * Web: https://infinilabs.com + * Email: hello#infini.ltd */ + +package common + +import ( + "bytes" + "fmt" + "infini.sh/console/model/alerting" + "infini.sh/console/service/alerting/action" + "infini.sh/console/service/alerting/funcs" + "infini.sh/framework/core/orm" + "text/template" +) + +func PerformChannel(channel *alerting.Channel, ctx map[string]interface{}) ([]byte, error, []byte) { + var ( + act action.Action + message []byte + err error + ) + channel, err = RetrieveChannel(channel) + if err != nil { + return nil, err, nil + } + switch channel.Type { + + case alerting.ChannelWebhook: + message, err = ResolveMessage(channel.Webhook.Body, ctx) + if err != nil { + return nil, err, message + } + wh := *channel.Webhook + urlBytes, err := ResolveMessage(wh.URL, ctx) + if err != nil { + return nil, err, message + } + wh.URL = string(urlBytes) + act = &action.WebhookAction{ + Data: &wh, + Message: string(message), + } + case alerting.ChannelEmail: + message, err = ResolveMessage(channel.Email.Body, ctx) + if err != nil { + return nil, err, message + } + subjectBytes, err := ResolveMessage(channel.Email.Subject, ctx) + if err != nil { + return nil, err, nil + } + act = &action.EmailAction{ + Data: channel.Email, + Subject: string(subjectBytes), + Body: string(message), + } + default: + return nil, fmt.Errorf("unsupported action type: %s", channel.Type), message + } + executeResult, err := act.Execute() + return executeResult, err, message +} + +func ResolveMessage(messageTemplate string, ctx map[string]interface{}) ([]byte, error){ + msg := messageTemplate + tmpl, err := template.New("alert-message").Funcs(funcs.GenericFuncMap()).Parse(msg) + if err !=nil { + return nil, fmt.Errorf("parse message temlate error: %w", err) + } + msgBuffer := &bytes.Buffer{} + err = tmpl.Execute(msgBuffer, ctx) + return msgBuffer.Bytes(), err +} + +func RetrieveChannel(ch *alerting.Channel) (*alerting.Channel, error) { + if ch == nil { + return nil, fmt.Errorf("empty channel") + } + if ch.ID != "" { + _, err := orm.Get(ch) + if err != nil { + return nil, err + } + } + return ch, nil +} \ No newline at end of file diff --git a/service/alerting/elasticsearch/engine.go b/service/alerting/elasticsearch/engine.go index 52a3882b..36394ebf 100644 --- a/service/alerting/elasticsearch/engine.go +++ b/service/alerting/elasticsearch/engine.go @@ -5,7 +5,6 @@ package elasticsearch import ( - "bytes" "context" "fmt" "github.com/Knetic/govaluate" @@ -13,8 +12,7 @@ import ( "infini.sh/console/model" "infini.sh/console/model/alerting" alerting2 "infini.sh/console/service/alerting" - "infini.sh/console/service/alerting/action" - "infini.sh/console/service/alerting/funcs" + "infini.sh/console/service/alerting/common" "infini.sh/framework/core/elastic" "infini.sh/console/model/insight" "infini.sh/framework/core/kv" @@ -25,7 +23,6 @@ import ( "sort" "strconv" "strings" - "text/template" "time" ) @@ -857,12 +854,12 @@ func attachTitleMessageToCtx(title, message string, paramsCtx map[string]interfa tplBytes []byte err error ) - tplBytes, err = resolveMessage(message, paramsCtx) + tplBytes, err = common.ResolveMessage(message, paramsCtx) if err != nil { return fmt.Errorf("resolve message template error: %w", err) } paramsCtx[alerting2.ParamMessage] = string(tplBytes) - tplBytes, err = resolveMessage(title, paramsCtx) + tplBytes, err = common.ResolveMessage(title, paramsCtx) if err != nil { return fmt.Errorf("resolve title template error: %w", err) } @@ -962,7 +959,7 @@ func performChannels(channels []alerting.Channel, ctx map[string]interface{}) ([ var errCount int var actionResults []alerting.ActionExecutionResult for _, channel := range channels { - resBytes, err, messageBytes := performChannel(&channel, ctx) + resBytes, err, messageBytes := common.PerformChannel(&channel, ctx) var errStr string if err != nil { errCount++ @@ -980,64 +977,8 @@ func performChannels(channels []alerting.Channel, ctx map[string]interface{}) ([ return actionResults, errCount } -func resolveMessage(messageTemplate string, ctx map[string]interface{}) ([]byte, error){ - msg := messageTemplate - tmpl, err := template.New("alert-message").Funcs(funcs.GenericFuncMap()).Parse(msg) - if err !=nil { - return nil, fmt.Errorf("parse message temlate error: %w", err) - } - msgBuffer := &bytes.Buffer{} - err = tmpl.Execute(msgBuffer, ctx) - return msgBuffer.Bytes(), err -} -func performChannel(channel *alerting.Channel, ctx map[string]interface{}) ([]byte, error, []byte) { - var ( - act action.Action - message []byte - err error - ) - channel, err = RetrieveChannel(channel) - if err != nil { - return nil, err, nil - } - switch channel.Type { - case alerting.ChannelWebhook: - message, err = resolveMessage(channel.Webhook.Body, ctx) - if err != nil { - return nil, err, message - } - wh := *channel.Webhook - urlBytes, err := resolveMessage(wh.URL, ctx) - if err != nil { - return nil, err, message - } - wh.URL = string(urlBytes) - act = &action.WebhookAction{ - Data: &wh, - Message: string(message), - } - case alerting.ChannelEmail: - message, err = resolveMessage(channel.Email.Body, ctx) - if err != nil { - return nil, err, message - } - subjectBytes, err := resolveMessage(channel.Email.Subject, ctx) - if err != nil { - return nil, err, nil - } - act = &action.EmailAction{ - Data: channel.Email, - Subject: string(subjectBytes), - Body: string(message), - } - default: - return nil, fmt.Errorf("unsupported action type: %s", channel.Type), message - } - executeResult, err := act.Execute() - return executeResult, err, message -} func (engine *Engine) GenerateTask(rule alerting.Rule) func(ctx context.Context) { return func(ctx context.Context) { defer func() { diff --git a/service/alerting/elasticsearch/helper.go b/service/alerting/elasticsearch/helper.go deleted file mode 100644 index f19ada63..00000000 --- a/service/alerting/elasticsearch/helper.go +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright © INFINI Ltd. All rights reserved. - * Web: https://infinilabs.com - * Email: hello#infini.ltd */ - -package elasticsearch - -import ( - "fmt" - "infini.sh/console/model/alerting" - "infini.sh/framework/core/orm" -) - -func RetrieveChannel(ch *alerting.Channel) (*alerting.Channel, error) { - if ch == nil { - return nil, fmt.Errorf("empty channel") - } - if ch.ID != "" { - _, err := orm.Get(ch) - if err != nil { - return nil, err - } - } - return ch, nil -}