diff --git a/center/router/router.go b/center/router/router.go index 602685dc3..003b9264f 100644 --- a/center/router/router.go +++ b/center/router/router.go @@ -324,6 +324,7 @@ func (rt *Router) Config(r *gin.Engine) { pages.GET("/alert-rule/:arid/pure", rt.auth(), rt.user(), rt.perm("/alert-rules"), rt.alertRulePureGet) pages.PUT("/busi-group/alert-rule/validate", rt.auth(), rt.user(), rt.perm("/alert-rules/put"), rt.alertRuleValidation) pages.POST("/relabel-test", rt.auth(), rt.user(), rt.relabelTest) + pages.POST("/busi-group/:id/alert-rules/clone", rt.auth(), rt.user(), rt.perm("/alert-rules/post"), rt.bgrw(), rt.cloneToMachine) pages.GET("/busi-groups/recording-rules", rt.auth(), rt.user(), rt.perm("/recording-rules"), rt.recordingRuleGetsByGids) pages.GET("/busi-group/:id/recording-rules", rt.auth(), rt.user(), rt.perm("/recording-rules"), rt.recordingRuleGets) diff --git a/center/router/router_alert_rule.go b/center/router/router_alert_rule.go index 5862b4ead..cbfd0bdce 100644 --- a/center/router/router_alert_rule.go +++ b/center/router/router_alert_rule.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "regexp" "strconv" "strings" "time" @@ -13,6 +14,7 @@ import ( "github.com/ccfos/nightingale/v6/pushgw/writer" "github.com/gin-gonic/gin" + "github.com/jinzhu/copier" "github.com/prometheus/prometheus/prompb" "github.com/toolkits/pkg/ginx" "github.com/toolkits/pkg/i18n" @@ -295,6 +297,17 @@ func (rt *Router) alertRulePutFields(c *gin.Context) { continue } + if f.Action == "update_triggers" { + if triggers, has := f.Fields["triggers"]; has { + originRule := ar.RuleConfigJson.(map[string]interface{}) + originRule["triggers"] = triggers + b, err := json.Marshal(originRule) + ginx.Dangerous(err) + ginx.Dangerous(ar.UpdateFieldsMap(rt.Ctx, map[string]interface{}{"rule_config": string(b)})) + continue + } + } + if f.Action == "annotations_add" { if annotations, has := f.Fields["annotations"]; has { annotationsMap := annotations.(map[string]interface{}) @@ -499,3 +512,84 @@ func (rt *Router) relabelTest(c *gin.Context) { ginx.NewRender(c).Data(tags, nil) } + +type identListForm struct { + Ids []int64 `json:"ids"` + IdentList []string `json:"ident_list"` +} + +func (rt *Router) cloneToMachine(c *gin.Context) { + var f identListForm + ginx.BindJSON(c, &f) + + if len(f.IdentList) == 0 { + ginx.Bomb(http.StatusBadRequest, "ident_list is empty") + } + + alertRules, err := models.AlertRuleGetsByIds(rt.Ctx, f.Ids) + ginx.Dangerous(err) + + re := regexp.MustCompile("ident = \".*?\"") + + user := c.MustGet("username").(string) + now := time.Now().Unix() + + newRules := make([]*models.AlertRule, 0) + + reterr := make(map[string]map[string]string) + + for i := range alertRules { + reterr[alertRules[i].Name] = make(map[string]string) + + if alertRules[i].Cate != "prometheus" { + reterr[alertRules[i].Name]["all"] = "Only Prometheus rules can be cloned to machines" + continue + } + var ruleConfig *models.PromRuleConfig + if err := json.Unmarshal([]byte(alertRules[i].RuleConfig), &ruleConfig); err != nil { + reterr[alertRules[i].Name]["all"] = "invalid rule, fail to unmarshal rule config" + continue + } + + for j := range f.IdentList { + + for q := range ruleConfig.Queries { + ruleConfig.Queries[q].PromQl = re.ReplaceAllString(ruleConfig.Queries[q].PromQl, fmt.Sprintf("ident = \"%s\"", f.IdentList[j])) + } + + configJson, err := json.Marshal(ruleConfig) + if err != nil { + reterr[alertRules[i].Name][f.IdentList[j]] = fmt.Sprintf("invalid rule, fail to marshal rule config, err: %s", err) + continue + } + + newRule := &models.AlertRule{} + if err := copier.Copy(newRule, alertRules[i]); err != nil { + reterr[alertRules[i].Name][f.IdentList[j]] = fmt.Sprintf("fail to clone rule, err: %s", err) + continue + } + newRule.Id = 0 + newRule.Name = alertRules[i].Name + "_" + f.IdentList[j] + newRule.CreateBy = user + newRule.UpdateBy = user + newRule.UpdateAt = now + newRule.CreateAt = now + newRule.RuleConfig = string(configJson) + + exist, err := models.AlertRuleExists(rt.Ctx, 0, newRule.GroupId, newRule.DatasourceIdsJson, newRule.Name) + if err != nil { + reterr[alertRules[i].Name][f.IdentList[j]] = err.Error() + continue + } + + if exist { + reterr[alertRules[i].Name][f.IdentList[j]] = fmt.Sprintf("rule already exists, ruleName: %s", newRule.Name) + continue + } + + newRules = append(newRules, newRule) + } + } + + ginx.NewRender(c).Data(reterr, models.InsertAlertRule(rt.Ctx, newRules)) +} diff --git a/go.mod b/go.mod index 6e33afc1a..937455d2f 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/golang/snappy v0.0.4 github.com/google/uuid v1.3.0 github.com/hashicorp/go-version v1.6.0 + github.com/jinzhu/copier v0.4.0 github.com/json-iterator/go v1.1.12 github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 github.com/mailru/easyjson v0.7.7 diff --git a/go.sum b/go.sum index 68755d698..deda3ab87 100644 --- a/go.sum +++ b/go.sum @@ -160,6 +160,8 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= +github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= diff --git a/models/alert_rule.go b/models/alert_rule.go index b91e84f05..0dc13b7c9 100644 --- a/models/alert_rule.go +++ b/models/alert_rule.go @@ -11,6 +11,7 @@ import ( "github.com/ccfos/nightingale/v6/pkg/poster" "github.com/ccfos/nightingale/v6/pushgw/pconf" + "github.com/jinzhu/copier" "github.com/pkg/errors" "github.com/toolkits/pkg/logger" "github.com/toolkits/pkg/str" @@ -1080,3 +1081,19 @@ func GetTargetsOfHostAlertRule(ctx *ctx.Context, engineName string) (map[string] return m, nil } + +func (ar *AlertRule) Copy(ctx *ctx.Context) (*AlertRule, error) { + newAr := &AlertRule{} + err := copier.Copy(newAr, ar) + if err != nil { + logger.Errorf("copy alert rule failed, %v", err) + } + return newAr, err +} + +func InsertAlertRule(ctx *ctx.Context, ars []*AlertRule) error { + if len(ars) == 0 { + return nil + } + return DB(ctx).Create(ars).Error +}