mirror of
https://github.com/grafana/grafana.git
synced 2025-01-27 16:57:14 -06:00
Alerting: Export of alert rules in HCL format (#73166)
* import hashicopr/hcl/v2 * add hcl package and export to HCL * annotate export structs --------- Co-authored-by: Konrad Lalik <konrad.lalik@grafana.com>
This commit is contained in:
parent
5d89c15851
commit
dce492642a
4
go.mod
4
go.mod
@ -68,6 +68,7 @@ require (
|
||||
github.com/hashicorp/go-hclog v1.5.0 // @grafana/plugins-platform-backend
|
||||
github.com/hashicorp/go-plugin v1.4.9 // @grafana/plugins-platform-backend
|
||||
github.com/hashicorp/go-version v1.6.0 // @grafana/backend-platform
|
||||
github.com/hashicorp/hcl/v2 v2.17.0 // @grafana/alerting-squad-backend
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.12.3 // @grafana/observability-metrics
|
||||
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // @grafana/grafana-app-platform-squad
|
||||
github.com/jmespath/go-jmespath v0.4.0 // @grafana/backend-platform
|
||||
@ -290,10 +291,12 @@ require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||
github.com/agext/levenshtein v1.2.1 // indirect
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect
|
||||
github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect
|
||||
github.com/apache/thrift v0.18.1 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/bmatcuk/doublestar v1.1.1 // indirect
|
||||
github.com/buildkite/yaml v2.1.0+incompatible // indirect
|
||||
@ -391,6 +394,7 @@ require (
|
||||
github.com/weaveworks/promrus v1.2.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/yuin/gopher-lua v1.1.0 // indirect
|
||||
github.com/zclconf/go-cty v1.13.0 // indirect
|
||||
github.com/zeebo/xxh3 v1.0.2 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.7 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect
|
||||
|
8
go.sum
8
go.sum
@ -672,6 +672,8 @@ github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f/go.mod h1:f3H
|
||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
|
||||
@ -711,6 +713,8 @@ github.com/apache/thrift v0.18.1 h1:lNhK/1nqjbwbiOPDBPFJVKxgDEGSepKuTh6OLiXW8kg=
|
||||
github.com/apache/thrift v0.18.1/go.mod h1:rdQn/dCcDKEWjjylUeueum4vQEjG2v8v2PqriUnbr+I=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
@ -1904,6 +1908,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl/v2 v2.17.0 h1:z1XvSUyXd1HP10U4lrLg5e0JMVz6CPaJvAgxM0KNZVY=
|
||||
github.com/hashicorp/hcl/v2 v2.17.0/go.mod h1:gJyW2PTShkJqQBKpAmPO3yxMxIuoXkOF2TpqXzrQyx4=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
@ -2906,6 +2912,8 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE=
|
||||
github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
|
||||
github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0=
|
||||
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/hcl"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
alerting_models "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
@ -498,7 +499,7 @@ func extractExportRequest(c *contextmodel.ReqContext) definitions.ExportQueryPar
|
||||
}
|
||||
|
||||
queryFormat := c.Query("format")
|
||||
if queryFormat == "yaml" || queryFormat == "json" {
|
||||
if queryFormat == "yaml" || queryFormat == "json" || queryFormat == "hcl" {
|
||||
format = queryFormat
|
||||
}
|
||||
|
||||
@ -512,6 +513,10 @@ func extractExportRequest(c *contextmodel.ReqContext) definitions.ExportQueryPar
|
||||
|
||||
func exportResponse(c *contextmodel.ReqContext, body definitions.AlertingFileExport) response.Response {
|
||||
params := extractExportRequest(c)
|
||||
if params.Format == "hcl" {
|
||||
return exportHcl(params.Download, body)
|
||||
}
|
||||
|
||||
if params.Download {
|
||||
r := response.JSONDownload
|
||||
if params.Format == "yaml" {
|
||||
@ -526,3 +531,43 @@ func exportResponse(c *contextmodel.ReqContext, body definitions.AlertingFileExp
|
||||
}
|
||||
return r(http.StatusOK, body)
|
||||
}
|
||||
|
||||
func exportHcl(download bool, body definitions.AlertingFileExport) response.Response {
|
||||
resources := make([]hcl.Resource, 0, len(body.Groups)+len(body.ContactPoints)+len(body.Policies))
|
||||
for idx, group := range body.Groups {
|
||||
gr := group
|
||||
resources = append(resources, hcl.Resource{
|
||||
Type: "grafana_rule_group",
|
||||
Name: fmt.Sprintf("rule_group_%04d", idx),
|
||||
Body: &gr,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO implement support.
|
||||
// for idx, cp := range ex.ContactPoints {
|
||||
// resources = append(resources, resourceBlock{
|
||||
// Type: "grafana_contact_point",
|
||||
// Name: fmt.Sprintf("contact_point_%d", idx),
|
||||
// Body: &cp,
|
||||
// })
|
||||
// }
|
||||
// for idx, cp := range ex.Policies {
|
||||
// resources = append(resources, resourceBlock{
|
||||
// Type: "grafana_notification_policy",
|
||||
// Name: fmt.Sprintf("notification_policy_%d", idx),
|
||||
// Body: &cp,
|
||||
// })
|
||||
//
|
||||
|
||||
hclBody, err := hcl.Encode(resources...)
|
||||
if err != nil {
|
||||
return response.Error(500, "body hcl encode", err)
|
||||
}
|
||||
resp := response.Respond(http.StatusOK, hclBody)
|
||||
if download {
|
||||
return resp.
|
||||
SetHeader("Content-Type", "application/terraform+hcl").
|
||||
SetHeader("Content-Disposition", `attachment;filename=export.tf`)
|
||||
}
|
||||
return resp.SetHeader("Content-Type", "text/hcl")
|
||||
}
|
||||
|
@ -566,6 +566,106 @@ func TestProvisioningApi(t *testing.T) {
|
||||
require.Equal(t, 200, response.Status())
|
||||
require.Equal(t, expectedResponse, string(response.Body()))
|
||||
})
|
||||
|
||||
t.Run("hcl body content is as expected", func(t *testing.T) {
|
||||
sut := createProvisioningSrvSut(t)
|
||||
rule1 := createTestAlertRule("rule1", 1)
|
||||
rule1.Labels = map[string]string{
|
||||
"test": "label",
|
||||
}
|
||||
rule1.Annotations = map[string]string{
|
||||
"test": "annotation",
|
||||
}
|
||||
rule1.NoDataState = definitions.Alerting
|
||||
rule1.ExecErrState = definitions.ErrorErrState
|
||||
insertRule(t, sut, rule1)
|
||||
insertRule(t, sut, createTestAlertRule("rule2", 1))
|
||||
|
||||
expectedResponse := `resource "grafana_rule_group" "rule_group_0000" {
|
||||
org_id = 1
|
||||
name = "my-cool-group"
|
||||
folder_uid = "folder-uid"
|
||||
interval_seconds = 60
|
||||
|
||||
rule {
|
||||
name = "rule1"
|
||||
condition = "A"
|
||||
|
||||
data {
|
||||
ref_id = "A"
|
||||
query_type = ""
|
||||
|
||||
relative_time_range {
|
||||
from = 0
|
||||
to = 0
|
||||
}
|
||||
|
||||
datasource_uid = ""
|
||||
model = "{\"conditions\":[{\"evaluator\":{\"params\":[3],\"type\":\"gt\"},\"operator\":{\"type\":\"and\"},\"query\":{\"params\":[\"A\"]},\"reducer\":{\"type\":\"last\"},\"type\":\"query\"}],\"datasource\":{\"type\":\"__expr__\",\"uid\":\"__expr__\"},\"expression\":\"1==0\",\"intervalMs\":1000,\"maxDataPoints\":43200,\"refId\":\"A\",\"type\":\"math\"}"
|
||||
}
|
||||
|
||||
no_data_state = "Alerting"
|
||||
exec_err_state = "Error"
|
||||
for = 0
|
||||
annotations = {
|
||||
test = "annotation"
|
||||
}
|
||||
labels = {
|
||||
test = "label"
|
||||
}
|
||||
is_paused = false
|
||||
}
|
||||
rule {
|
||||
name = "rule2"
|
||||
condition = "A"
|
||||
|
||||
data {
|
||||
ref_id = "A"
|
||||
query_type = ""
|
||||
|
||||
relative_time_range {
|
||||
from = 0
|
||||
to = 0
|
||||
}
|
||||
|
||||
datasource_uid = ""
|
||||
model = "{\"conditions\":[{\"evaluator\":{\"params\":[3],\"type\":\"gt\"},\"operator\":{\"type\":\"and\"},\"query\":{\"params\":[\"A\"]},\"reducer\":{\"type\":\"last\"},\"type\":\"query\"}],\"datasource\":{\"type\":\"__expr__\",\"uid\":\"__expr__\"},\"expression\":\"1==0\",\"intervalMs\":1000,\"maxDataPoints\":43200,\"refId\":\"A\",\"type\":\"math\"}"
|
||||
}
|
||||
|
||||
no_data_state = "OK"
|
||||
exec_err_state = "OK"
|
||||
for = 0
|
||||
annotations = null
|
||||
labels = null
|
||||
is_paused = false
|
||||
}
|
||||
}
|
||||
`
|
||||
rc := createTestRequestCtx()
|
||||
rc.Context.Req.Form.Set("format", "hcl")
|
||||
rc.Context.Req.Form.Set("download", "false")
|
||||
|
||||
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
||||
response.WriteTo(&rc)
|
||||
|
||||
require.Equal(t, 200, response.Status())
|
||||
require.Equal(t, expectedResponse, string(response.Body()))
|
||||
require.Equal(t, "text/hcl", rc.Resp.Header().Get("Content-Type"))
|
||||
|
||||
t.Run("and add specific headers if download=true", func(t *testing.T) {
|
||||
rc := createTestRequestCtx()
|
||||
rc.Context.Req.Form.Set("format", "hcl")
|
||||
rc.Context.Req.Form.Set("download", "true")
|
||||
|
||||
response := sut.RouteGetAlertRuleGroupExport(&rc, "folder-uid", "my-cool-group")
|
||||
response.WriteTo(&rc)
|
||||
|
||||
require.Equal(t, 200, response.Status())
|
||||
require.Equal(t, expectedResponse, string(response.Body()))
|
||||
require.Equal(t, "application/terraform+hcl", rc.Resp.Header().Get("Content-Type"))
|
||||
require.Equal(t, `attachment;filename=export.tf`, rc.Resp.Header().Get("Content-Disposition"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("alert rule", func(t *testing.T) {
|
||||
|
@ -151,11 +151,13 @@ func AlertRuleGroupExportFromAlertRuleGroupWithFolderTitle(d models.AlertRuleGro
|
||||
rules = append(rules, alert)
|
||||
}
|
||||
return definitions.AlertRuleGroupExport{
|
||||
OrgID: d.OrgID,
|
||||
Name: d.Title,
|
||||
Folder: d.FolderTitle,
|
||||
Interval: model.Duration(time.Duration(d.Interval) * time.Second),
|
||||
Rules: rules,
|
||||
OrgID: d.OrgID,
|
||||
Name: d.Title,
|
||||
Folder: d.FolderTitle,
|
||||
FolderUID: d.FolderUID,
|
||||
Interval: model.Duration(time.Duration(d.Interval) * time.Second),
|
||||
IntervalSeconds: d.Interval,
|
||||
Rules: rules,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -184,6 +186,7 @@ func AlertRuleExportFromAlertRule(rule models.AlertRule) (definitions.AlertRuleE
|
||||
UID: rule.UID,
|
||||
Title: rule.Title,
|
||||
For: model.Duration(rule.For),
|
||||
ForSeconds: int64(rule.For.Seconds()),
|
||||
Condition: rule.Condition,
|
||||
Data: data,
|
||||
DashboardUID: dashboardUID,
|
||||
@ -207,12 +210,13 @@ func AlertQueryExportFromAlertQuery(query models.AlertQuery) (definitions.AlertQ
|
||||
return definitions.AlertQueryExport{
|
||||
RefID: query.RefID,
|
||||
QueryType: query.QueryType,
|
||||
RelativeTimeRange: definitions.RelativeTimeRange{
|
||||
From: definitions.Duration(query.RelativeTimeRange.From),
|
||||
To: definitions.Duration(query.RelativeTimeRange.To),
|
||||
RelativeTimeRange: definitions.RelativeTimeRangeExport{
|
||||
FromSeconds: int64(time.Duration(query.RelativeTimeRange.From).Seconds()),
|
||||
ToSeconds: int64(time.Duration(query.RelativeTimeRange.To).Seconds()),
|
||||
},
|
||||
DatasourceUID: query.DatasourceUID,
|
||||
Model: mdl,
|
||||
ModelString: string(query.Model),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
30
pkg/services/ngalert/api/hcl/hcl.go
Normal file
30
pkg/services/ngalert/api/hcl/hcl.go
Normal file
@ -0,0 +1,30 @@
|
||||
package hcl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||
)
|
||||
|
||||
type Resource struct {
|
||||
Type string `hcl:"type,label"`
|
||||
Name string `hcl:"name,label"`
|
||||
Body interface{} `hcl:",block"`
|
||||
}
|
||||
|
||||
func Encode(resources ...Resource) (data []byte, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("failed to encode struct to HCL: %v", r)
|
||||
}
|
||||
}()
|
||||
f := hclwrite.NewEmptyFile()
|
||||
|
||||
for _, resource := range resources {
|
||||
blk := gohcl.EncodeAsBlock(resource.Body, "resource")
|
||||
blk.SetLabels([]string{resource.Type, resource.Name})
|
||||
f.Body().AppendBlock(blk)
|
||||
}
|
||||
return f.Bytes(), nil
|
||||
}
|
75
pkg/services/ngalert/api/hcl/hcl_test.go
Normal file
75
pkg/services/ngalert/api/hcl/hcl_test.go
Normal file
@ -0,0 +1,75 @@
|
||||
package hcl
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
type data struct {
|
||||
Name string `hcl:"name"`
|
||||
Number float64 `hcl:"number"`
|
||||
NumberRef *float64 `hcl:"numberRef"`
|
||||
Bool bool `hcl:"bul"`
|
||||
BoolRef *bool `hcl:"bulRef"`
|
||||
Ignored string
|
||||
Blocks []data `hcl:"blocks,block"`
|
||||
SubData *data `hcl:"sub,block"`
|
||||
}
|
||||
|
||||
encoded, err := Encode(Resource{
|
||||
Type: "grafana_test",
|
||||
Name: "test-01",
|
||||
Body: &data{
|
||||
Name: "test",
|
||||
Number: 123,
|
||||
NumberRef: func(f float64) *float64 { return &f }(1333),
|
||||
Bool: false,
|
||||
BoolRef: func(f bool) *bool { return &f }(true),
|
||||
Ignored: "Ignore me",
|
||||
Blocks: []data{
|
||||
{
|
||||
Name: "el-0",
|
||||
Number: 1,
|
||||
},
|
||||
{
|
||||
Name: "el-1",
|
||||
Number: 2,
|
||||
Bool: true,
|
||||
},
|
||||
},
|
||||
SubData: &data{
|
||||
Name: "sub-data",
|
||||
Number: 123123,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
t.Log(string(encoded))
|
||||
require.Equal(t, `resource "grafana_test" "test-01" {
|
||||
name = "test"
|
||||
number = 123
|
||||
numberRef = 1333
|
||||
bul = false
|
||||
bulRef = true
|
||||
|
||||
blocks {
|
||||
name = "el-0"
|
||||
number = 1
|
||||
bul = false
|
||||
}
|
||||
blocks {
|
||||
name = "el-1"
|
||||
number = 2
|
||||
bul = true
|
||||
}
|
||||
|
||||
sub {
|
||||
name = "sub-data"
|
||||
number = 123123
|
||||
bul = false
|
||||
}
|
||||
}
|
||||
`, string(encoded))
|
||||
}
|
@ -218,34 +218,43 @@ type AlertRuleGroup struct {
|
||||
|
||||
// AlertRuleGroupExport is the provisioned file export of AlertRuleGroupV1.
|
||||
type AlertRuleGroupExport struct {
|
||||
OrgID int64 `json:"orgId" yaml:"orgId"`
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Folder string `json:"folder" yaml:"folder"`
|
||||
Interval model.Duration `json:"interval" yaml:"interval"`
|
||||
Rules []AlertRuleExport `json:"rules" yaml:"rules"`
|
||||
OrgID int64 `json:"orgId" yaml:"orgId" hcl:"org_id"`
|
||||
Name string `json:"name" yaml:"name" hcl:"name"`
|
||||
Folder string `json:"folder" yaml:"folder"`
|
||||
FolderUID string `json:"-" yaml:"-" hcl:"folder_uid"`
|
||||
Interval model.Duration `json:"interval" yaml:"interval"`
|
||||
IntervalSeconds int64 `json:"-" yaml:"-" hcl:"interval_seconds"`
|
||||
Rules []AlertRuleExport `json:"rules" yaml:"rules" hcl:"rule,block"`
|
||||
}
|
||||
|
||||
// AlertRuleExport is the provisioned file export of models.AlertRule.
|
||||
type AlertRuleExport struct {
|
||||
UID string `json:"uid" yaml:"uid"`
|
||||
Title string `json:"title" yaml:"title"`
|
||||
Condition string `json:"condition" yaml:"condition"`
|
||||
Data []AlertQueryExport `json:"data" yaml:"data"`
|
||||
Title string `json:"title" yaml:"title" hcl:"name"`
|
||||
Condition string `json:"condition" yaml:"condition" hcl:"condition"`
|
||||
Data []AlertQueryExport `json:"data" yaml:"data" hcl:"data,block"`
|
||||
DashboardUID string `json:"dasboardUid,omitempty" yaml:"dashboardUid,omitempty"`
|
||||
PanelID int64 `json:"panelId,omitempty" yaml:"panelId,omitempty"`
|
||||
NoDataState NoDataState `json:"noDataState" yaml:"noDataState"`
|
||||
ExecErrState ExecutionErrorState `json:"execErrState" yaml:"execErrState"`
|
||||
NoDataState NoDataState `json:"noDataState" yaml:"noDataState" hcl:"no_data_state"`
|
||||
ExecErrState ExecutionErrorState `json:"execErrState" yaml:"execErrState" hcl:"exec_err_state"`
|
||||
For model.Duration `json:"for" yaml:"for"`
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
|
||||
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
|
||||
IsPaused bool `json:"isPaused" yaml:"isPaused"`
|
||||
ForSeconds int64 `json:"-" yaml:"-" hcl:"for"`
|
||||
Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty" hcl:"annotations"`
|
||||
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty" hcl:"labels"`
|
||||
IsPaused bool `json:"isPaused" yaml:"isPaused" hcl:"is_paused"`
|
||||
}
|
||||
|
||||
// AlertQueryExport is the provisioned export of models.AlertQuery.
|
||||
type AlertQueryExport struct {
|
||||
RefID string `json:"refId" yaml:"refId"`
|
||||
QueryType string `json:"queryType,omitempty" yaml:"queryType,omitempty"`
|
||||
RelativeTimeRange RelativeTimeRange `json:"relativeTimeRange,omitempty" yaml:"relativeTimeRange,omitempty"`
|
||||
DatasourceUID string `json:"datasourceUid" yaml:"datasourceUid"`
|
||||
Model map[string]any `json:"model" yaml:"model"`
|
||||
RefID string `json:"refId" yaml:"refId" hcl:"ref_id"`
|
||||
QueryType string `json:"queryType,omitempty" yaml:"queryType,omitempty" hcl:"query_type"`
|
||||
RelativeTimeRange RelativeTimeRangeExport `json:"relativeTimeRange,omitempty" yaml:"relativeTimeRange,omitempty" hcl:"relative_time_range,block"`
|
||||
DatasourceUID string `json:"datasourceUid" yaml:"datasourceUid" hcl:"datasource_uid"`
|
||||
Model map[string]any `json:"model" yaml:"model"`
|
||||
ModelString string `json:"-" yaml:"-" hcl:"model"`
|
||||
}
|
||||
|
||||
type RelativeTimeRangeExport struct {
|
||||
FromSeconds int64 `json:"from" yaml:"from" hcl:"from"`
|
||||
ToSeconds int64 `json:"to" yaml:"to" hcl:"to"`
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ export interface Datasource {
|
||||
export const PREVIEW_URL = '/api/v1/rule/test/grafana';
|
||||
export const PROM_RULES_URL = 'api/prometheus/grafana/api/v1/rules';
|
||||
|
||||
function getProvisioningUrl(ruleUid: string, format: 'yaml' | 'json' = 'yaml') {
|
||||
function getProvisioningUrl(ruleUid: string, format: RuleExportFormats = 'yaml') {
|
||||
return `/api/v1/provisioning/alert-rules/${ruleUid}/export?format=${format}`;
|
||||
}
|
||||
|
||||
|
@ -21,16 +21,15 @@ const YamlRuleExportProvider: RuleExportProvider<'yaml'> = {
|
||||
exportFormat: 'yaml',
|
||||
};
|
||||
|
||||
// TODO Waiting for BE changes
|
||||
// const HclRuleExportProvider: RuleExportProvider<'hcl'> = {
|
||||
// name: 'HCL',
|
||||
// exportFormat: 'hcl',
|
||||
// };
|
||||
const HclRuleExportProvider: RuleExportProvider<'hcl'> = {
|
||||
name: 'Terraform (HCL)',
|
||||
exportFormat: 'hcl',
|
||||
};
|
||||
|
||||
export const grafanaRuleExportProviders = {
|
||||
[JsonRuleExportProvider.exportFormat]: JsonRuleExportProvider,
|
||||
[YamlRuleExportProvider.exportFormat]: YamlRuleExportProvider,
|
||||
// [HclRuleExportProvider.exportFormat]: HclRuleExportProvider,
|
||||
[HclRuleExportProvider.exportFormat]: HclRuleExportProvider,
|
||||
} as const;
|
||||
|
||||
export type RuleExportFormats = keyof typeof grafanaRuleExportProviders;
|
||||
|
Loading…
Reference in New Issue
Block a user