Alerting: Rename expression elements of Rules APIs (#110914)
This renames `data` to `expressions` for clarity in the rules apis. Also makes certain fields that are redundant optional in the case of pure expressions, so that users don't have to specify them when they are not needed (e.g. not datasource queries).
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
@@ -43,8 +44,8 @@ func convertToK8sResource(
|
||||
Labels: make(map[string]string),
|
||||
},
|
||||
Spec: model.AlertRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: make(map[string]model.AlertRuleQuery),
|
||||
Title: rule.Title,
|
||||
Expressions: make(model.AlertRuleExpressionMap),
|
||||
Trigger: model.AlertRuleIntervalTrigger{
|
||||
Interval: model.AlertRulePromDuration(interval.String()),
|
||||
},
|
||||
@@ -90,19 +91,7 @@ func convertToK8sResource(
|
||||
}
|
||||
|
||||
for _, query := range rule.Data {
|
||||
k8sQuery := model.AlertRuleQuery{
|
||||
QueryType: query.QueryType,
|
||||
Model: query.Model,
|
||||
DatasourceUID: model.AlertRuleDatasourceUID(query.DatasourceUID),
|
||||
Source: util.Pointer(rule.Condition == query.RefID),
|
||||
}
|
||||
if time.Duration(query.RelativeTimeRange.From) > 0 || time.Duration(query.RelativeTimeRange.To) > 0 {
|
||||
k8sQuery.RelativeTimeRange = &model.AlertRuleRelativeTimeRange{
|
||||
From: model.AlertRulePromDurationWMillis(query.RelativeTimeRange.From.String()),
|
||||
To: model.AlertRulePromDurationWMillis(query.RelativeTimeRange.To.String()),
|
||||
}
|
||||
}
|
||||
k8sRule.Spec.Data[query.RefID] = k8sQuery
|
||||
k8sRule.Spec.Expressions[query.RefID] = convertToK8sExpression(query, rule)
|
||||
}
|
||||
|
||||
for _, setting := range rule.NotificationSettings {
|
||||
@@ -158,6 +147,29 @@ func convertToK8sResource(
|
||||
return k8sRule, nil
|
||||
}
|
||||
|
||||
func convertToK8sExpression(query ngmodels.AlertQuery, rule *ngmodels.AlertRule) model.AlertRuleExpression {
|
||||
expression := model.AlertRuleExpression{
|
||||
Model: query.Model,
|
||||
}
|
||||
if query.QueryType != "" {
|
||||
expression.QueryType = util.Pointer(query.QueryType)
|
||||
}
|
||||
// DatasourceUID is optional and defaults to expr datasource
|
||||
if !expr.IsDataSource(query.DatasourceUID) {
|
||||
expression.DatasourceUID = util.Pointer(model.AlertRuleDatasourceUID(query.DatasourceUID))
|
||||
}
|
||||
if time.Duration(query.RelativeTimeRange.From) > 0 || time.Duration(query.RelativeTimeRange.To) > 0 {
|
||||
expression.RelativeTimeRange = &model.AlertRuleRelativeTimeRange{
|
||||
From: model.AlertRulePromDurationWMillis(query.RelativeTimeRange.From.String()),
|
||||
To: model.AlertRulePromDurationWMillis(query.RelativeTimeRange.To.String()),
|
||||
}
|
||||
}
|
||||
if rule.Condition == query.RefID {
|
||||
expression.Source = util.Pointer(true)
|
||||
}
|
||||
return expression
|
||||
}
|
||||
|
||||
func convertToK8sResources(
|
||||
orgID int64,
|
||||
rules []*ngmodels.AlertRule,
|
||||
@@ -201,7 +213,7 @@ func convertToBaseDomainModel(orgID int64, k8sRule *model.AlertRule) (*ngmodels.
|
||||
UID: k8sRule.Name,
|
||||
Title: k8sRule.Spec.Title,
|
||||
NamespaceUID: k8sRule.Namespace,
|
||||
Data: make([]ngmodels.AlertQuery, 0, len(k8sRule.Spec.Data)),
|
||||
Data: make([]ngmodels.AlertQuery, 0, len(k8sRule.Spec.Expressions)),
|
||||
IsPaused: k8sRule.Spec.Paused != nil && *k8sRule.Spec.Paused,
|
||||
Labels: make(map[string]string),
|
||||
Annotations: make(map[string]string),
|
||||
@@ -267,13 +279,13 @@ func convertToBaseDomainModel(orgID int64, k8sRule *model.AlertRule) (*ngmodels.
|
||||
}
|
||||
domainRule.IntervalSeconds = int64(time.Duration(interval).Seconds())
|
||||
|
||||
for refID, query := range k8sRule.Spec.Data {
|
||||
domainQuery, err := convertToDomainQuery(query, refID)
|
||||
for refID, expression := range k8sRule.Spec.Expressions {
|
||||
domainQuery, err := convertToDomainQuery(expression, refID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
domainRule.Data = append(domainRule.Data, domainQuery)
|
||||
if query.Source != nil && *query.Source {
|
||||
if expression.Source != nil && *expression.Source {
|
||||
if domainRule.Condition != "" {
|
||||
return nil, fmt.Errorf("multiple queries marked as source: %s and %s", domainRule.Condition, refID)
|
||||
}
|
||||
@@ -339,23 +351,29 @@ func convertNotificationSettings(sourceSettings *model.AlertRuleV0alpha1SpecNoti
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
func convertToDomainQuery(query model.AlertRuleQuery, refID string) (ngmodels.AlertQuery, error) {
|
||||
modelJson, err := json.Marshal(query.Model)
|
||||
func convertToDomainQuery(expression model.AlertRuleExpression, refID string) (ngmodels.AlertQuery, error) {
|
||||
modelJson, err := json.Marshal(expression.Model)
|
||||
if err != nil {
|
||||
return ngmodels.AlertQuery{}, fmt.Errorf("failed to marshal model: %w", err)
|
||||
}
|
||||
domainQuery := ngmodels.AlertQuery{
|
||||
RefID: refID,
|
||||
QueryType: query.QueryType,
|
||||
DatasourceUID: string(query.DatasourceUID),
|
||||
Model: modelJson,
|
||||
RefID: refID,
|
||||
Model: modelJson,
|
||||
}
|
||||
if query.RelativeTimeRange != nil {
|
||||
from, err := prom_model.ParseDuration(string(query.RelativeTimeRange.From))
|
||||
if expression.QueryType != nil {
|
||||
domainQuery.QueryType = *expression.QueryType
|
||||
}
|
||||
if expression.DatasourceUID != nil {
|
||||
domainQuery.DatasourceUID = string(*expression.DatasourceUID)
|
||||
} else {
|
||||
domainQuery.DatasourceUID = expr.DatasourceUID
|
||||
}
|
||||
if expression.RelativeTimeRange != nil {
|
||||
from, err := prom_model.ParseDuration(string(expression.RelativeTimeRange.From))
|
||||
if err != nil {
|
||||
return ngmodels.AlertQuery{}, fmt.Errorf("failed to parse duration: %w", err)
|
||||
}
|
||||
to, err := prom_model.ParseDuration(string(query.RelativeTimeRange.To))
|
||||
to, err := prom_model.ParseDuration(string(expression.RelativeTimeRange.To))
|
||||
if err != nil {
|
||||
return ngmodels.AlertQuery{}, fmt.Errorf("failed to parse duration: %w", err)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
model "github.com/grafana/grafana/apps/alerting/rules/pkg/apis/alerting/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
gapiutil "github.com/grafana/grafana/pkg/services/apiserver/utils"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
@@ -42,8 +43,8 @@ func convertToK8sResource(
|
||||
Labels: make(map[string]string),
|
||||
},
|
||||
Spec: model.RecordingRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: make(map[string]model.RecordingRuleQuery),
|
||||
Title: rule.Title,
|
||||
Expressions: make(model.RecordingRuleExpressionMap),
|
||||
Trigger: model.RecordingRuleIntervalTrigger{
|
||||
Interval: model.RecordingRulePromDuration(interval.String()),
|
||||
},
|
||||
@@ -67,21 +68,7 @@ func convertToK8sResource(
|
||||
}
|
||||
|
||||
for _, query := range rule.Data {
|
||||
k8sQuery := model.RecordingRuleQuery{
|
||||
QueryType: query.QueryType,
|
||||
Model: query.Model,
|
||||
DatasourceUID: model.RecordingRuleDatasourceUID(query.DatasourceUID),
|
||||
}
|
||||
if time.Duration(query.RelativeTimeRange.From) > 0 || time.Duration(query.RelativeTimeRange.To) > 0 {
|
||||
k8sQuery.RelativeTimeRange = &model.RecordingRuleRelativeTimeRange{
|
||||
From: model.RecordingRulePromDurationWMillis(query.RelativeTimeRange.From.String()),
|
||||
To: model.RecordingRulePromDurationWMillis(query.RelativeTimeRange.To.String()),
|
||||
}
|
||||
}
|
||||
if rule.Record != nil && rule.Record.From == query.RefID {
|
||||
k8sQuery.Source = util.Pointer(true)
|
||||
}
|
||||
k8sRule.Spec.Data[query.RefID] = k8sQuery
|
||||
k8sRule.Spec.Expressions[query.RefID] = convertToK8sExpression(query, rule)
|
||||
}
|
||||
|
||||
meta, err := utils.MetaAccessor(k8sRule)
|
||||
@@ -108,6 +95,29 @@ func convertToK8sResource(
|
||||
return k8sRule, nil
|
||||
}
|
||||
|
||||
func convertToK8sExpression(query ngmodels.AlertQuery, rule *ngmodels.AlertRule) model.RecordingRuleExpression {
|
||||
expression := model.RecordingRuleExpression{
|
||||
Model: query.Model,
|
||||
}
|
||||
if query.QueryType != "" {
|
||||
expression.QueryType = util.Pointer(query.QueryType)
|
||||
}
|
||||
// DatasourceUID is optional and defaults to expr datasource
|
||||
if !expr.IsDataSource(query.DatasourceUID) {
|
||||
expression.DatasourceUID = util.Pointer(model.RecordingRuleDatasourceUID(query.DatasourceUID))
|
||||
}
|
||||
if time.Duration(query.RelativeTimeRange.From) > 0 || time.Duration(query.RelativeTimeRange.To) > 0 {
|
||||
expression.RelativeTimeRange = &model.RecordingRuleRelativeTimeRange{
|
||||
From: model.RecordingRulePromDurationWMillis(query.RelativeTimeRange.From.String()),
|
||||
To: model.RecordingRulePromDurationWMillis(query.RelativeTimeRange.To.String()),
|
||||
}
|
||||
}
|
||||
if rule.Record != nil && rule.Record.From == query.RefID {
|
||||
expression.Source = util.Pointer(true)
|
||||
}
|
||||
return expression
|
||||
}
|
||||
|
||||
func convertToK8sResources(
|
||||
orgID int64,
|
||||
rules []*ngmodels.AlertRule,
|
||||
@@ -150,7 +160,7 @@ func convertToBaseDomainModel(orgID int64, k8sRule *model.RecordingRule) (*ngmod
|
||||
OrgID: orgID,
|
||||
UID: k8sRule.Name,
|
||||
Title: k8sRule.Spec.Title,
|
||||
Data: make([]ngmodels.AlertQuery, 0, len(k8sRule.Spec.Data)),
|
||||
Data: make([]ngmodels.AlertQuery, 0, len(k8sRule.Spec.Expressions)),
|
||||
IsPaused: k8sRule.Spec.Paused != nil && *k8sRule.Spec.Paused,
|
||||
Labels: make(map[string]string),
|
||||
|
||||
@@ -187,35 +197,13 @@ func convertToBaseDomainModel(orgID int64, k8sRule *model.RecordingRule) (*ngmod
|
||||
for k, v := range k8sRule.Spec.Labels {
|
||||
domainRule.Labels[k] = string(v)
|
||||
}
|
||||
for refID, query := range k8sRule.Spec.Data {
|
||||
modelJson, err := json.Marshal(query.Model)
|
||||
for refID, expression := range k8sRule.Spec.Expressions {
|
||||
domainQuery, err := convertToDomainQuery(expression, refID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal model: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
domainQuery := ngmodels.AlertQuery{
|
||||
RefID: refID,
|
||||
QueryType: query.QueryType,
|
||||
DatasourceUID: string(query.DatasourceUID),
|
||||
Model: modelJson,
|
||||
}
|
||||
if query.RelativeTimeRange != nil {
|
||||
from, err := prom_model.ParseDuration(string(query.RelativeTimeRange.From))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse duration: %w", err)
|
||||
}
|
||||
to, err := prom_model.ParseDuration(string(query.RelativeTimeRange.To))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse duration: %w", err)
|
||||
}
|
||||
domainQuery.RelativeTimeRange = ngmodels.RelativeTimeRange{
|
||||
From: ngmodels.Duration(from),
|
||||
To: ngmodels.Duration(to),
|
||||
}
|
||||
}
|
||||
|
||||
domainRule.Data = append(domainRule.Data, domainQuery)
|
||||
|
||||
if query.Source != nil && *query.Source {
|
||||
if expression.Source != nil && *expression.Source {
|
||||
if domainRule.Record.From != "" {
|
||||
return nil, fmt.Errorf("multiple queries marked as source: %s and %s", domainRule.Record.From, refID)
|
||||
}
|
||||
@@ -227,3 +215,37 @@ func convertToBaseDomainModel(orgID int64, k8sRule *model.RecordingRule) (*ngmod
|
||||
}
|
||||
return domainRule, nil
|
||||
}
|
||||
|
||||
func convertToDomainQuery(expression model.RecordingRuleExpression, refID string) (ngmodels.AlertQuery, error) {
|
||||
modelJson, err := json.Marshal(expression.Model)
|
||||
if err != nil {
|
||||
return ngmodels.AlertQuery{}, fmt.Errorf("failed to marshal model: %w", err)
|
||||
}
|
||||
domainQuery := ngmodels.AlertQuery{
|
||||
RefID: refID,
|
||||
Model: modelJson,
|
||||
}
|
||||
if expression.QueryType != nil {
|
||||
domainQuery.QueryType = *expression.QueryType
|
||||
}
|
||||
if expression.DatasourceUID != nil {
|
||||
domainQuery.DatasourceUID = string(*expression.DatasourceUID)
|
||||
} else {
|
||||
domainQuery.DatasourceUID = expr.DatasourceUID
|
||||
}
|
||||
if expression.RelativeTimeRange != nil {
|
||||
from, err := prom_model.ParseDuration(string(expression.RelativeTimeRange.From))
|
||||
if err != nil {
|
||||
return ngmodels.AlertQuery{}, fmt.Errorf("failed to parse duration: %w", err)
|
||||
}
|
||||
to, err := prom_model.ParseDuration(string(expression.RelativeTimeRange.To))
|
||||
if err != nil {
|
||||
return ngmodels.AlertQuery{}, fmt.Errorf("failed to parse duration: %w", err)
|
||||
}
|
||||
domainQuery.RelativeTimeRange = ngmodels.RelativeTimeRange{
|
||||
From: ngmodels.Duration(from),
|
||||
To: ngmodels.Duration(to),
|
||||
}
|
||||
}
|
||||
return domainQuery, nil
|
||||
}
|
||||
|
||||
@@ -54,10 +54,10 @@ func TestIntegrationResourceIdentifier(t *testing.T) {
|
||||
},
|
||||
Spec: v0alpha1.AlertRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: map[string]v0alpha1.AlertRuleQuery{
|
||||
Expressions: v0alpha1.AlertRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer("query"),
|
||||
DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{
|
||||
@@ -154,10 +154,10 @@ func TestIntegrationAccessControl(t *testing.T) {
|
||||
},
|
||||
Spec: v0alpha1.AlertRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: map[string]v0alpha1.AlertRuleQuery{
|
||||
Expressions: v0alpha1.AlertRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{
|
||||
@@ -241,10 +241,10 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
},
|
||||
Spec: v0alpha1.AlertRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: map[string]v0alpha1.AlertRuleQuery{
|
||||
Expressions: v0alpha1.AlertRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{
|
||||
@@ -295,10 +295,10 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
},
|
||||
Spec: v0alpha1.AlertRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: map[string]v0alpha1.AlertRuleQuery{
|
||||
Expressions: v0alpha1.AlertRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{
|
||||
@@ -328,8 +328,8 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Spec: v0alpha1.AlertRuleSpec{
|
||||
Title: "invalid-rule",
|
||||
Data: map[string]v0alpha1.AlertRuleQuery{}, // Empty data should fail
|
||||
Title: "invalid-rule",
|
||||
Expressions: v0alpha1.AlertRuleExpressionMap{}, // Empty data should fail
|
||||
Trigger: v0alpha1.AlertRuleIntervalTrigger{
|
||||
Interval: "30",
|
||||
},
|
||||
@@ -356,10 +356,10 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
},
|
||||
Spec: v0alpha1.AlertRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: map[string]v0alpha1.AlertRuleQuery{
|
||||
Expressions: v0alpha1.AlertRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{
|
||||
@@ -408,10 +408,10 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
},
|
||||
Spec: v0alpha1.AlertRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: map[string]v0alpha1.AlertRuleQuery{
|
||||
Expressions: v0alpha1.AlertRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{
|
||||
From: v0alpha1.AlertRulePromDurationWMillis("5m"),
|
||||
@@ -445,10 +445,10 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
},
|
||||
Spec: v0alpha1.AlertRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: map[string]v0alpha1.AlertRuleQuery{
|
||||
Expressions: v0alpha1.AlertRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{
|
||||
@@ -501,10 +501,10 @@ func TestIntegrationPatch(t *testing.T) {
|
||||
},
|
||||
Spec: v0alpha1.AlertRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: map[string]v0alpha1.AlertRuleQuery{
|
||||
Expressions: v0alpha1.AlertRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{
|
||||
|
||||
@@ -57,10 +57,10 @@ func TestIntegrationAlertRuleCompatCreateViaK8s(t *testing.T) {
|
||||
},
|
||||
Spec: v0alpha1.AlertRuleSpec{
|
||||
Title: rule.Title,
|
||||
Data: map[string]v0alpha1.AlertRuleQuery{
|
||||
Expressions: v0alpha1.AlertRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.AlertRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.AlertRuleRelativeTimeRange{
|
||||
@@ -91,9 +91,9 @@ func TestIntegrationAlertRuleCompatCreateViaK8s(t *testing.T) {
|
||||
err := json.Unmarshal(retrievedRule.Data[0].Model, &model)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, model)
|
||||
expectedModel, ok := created.Spec.Data["A"].Model.(map[string]interface{})
|
||||
expectedModel, ok := created.Spec.Expressions["A"].Model.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", created.Spec.Data["A"].Model)
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", created.Spec.Expressions["A"].Model)
|
||||
}
|
||||
for k, v := range expectedModel {
|
||||
require.EqualValues(t, v, model[k], "Model field %s should match", k)
|
||||
@@ -230,13 +230,13 @@ func TestIntegrationAlertRuleCompatCreateViaProvisioning(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, retrievedRule)
|
||||
require.Equal(t, r.Title, retrievedRule.Spec.Title)
|
||||
require.NotNil(t, retrievedRule.Spec.Data[r.Data[0].RefID].Source)
|
||||
require.True(t, *retrievedRule.Spec.Data[r.Data[0].RefID].Source)
|
||||
require.NotNil(t, retrievedRule.Spec.Expressions[r.Data[0].RefID].Source)
|
||||
require.True(t, *retrievedRule.Spec.Expressions[r.Data[0].RefID].Source)
|
||||
require.Equal(t, r.FolderUID, retrievedRule.Annotations["grafana.app/folder"])
|
||||
require.Equal(t, created.Title, retrievedRule.Labels[v0alpha1.GroupLabelKey])
|
||||
require.Equal(t, fmt.Sprintf("%d", i), retrievedRule.Labels[v0alpha1.GroupIndexLabelKey])
|
||||
require.Equal(t, ngmodels.ProvenanceAPI, ngmodels.Provenance(retrievedRule.GetProvenanceStatus()))
|
||||
require.EqualValues(t, r.Data[0].DatasourceUID, retrievedRule.Spec.Data["A"].DatasourceUID)
|
||||
require.EqualValues(t, r.Data[0].DatasourceUID, *retrievedRule.Spec.Expressions["A"].DatasourceUID)
|
||||
expectedDuration, err := prom_model.ParseDuration(fmt.Sprintf("%ds", created.Interval))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedDuration.String(), string(retrievedRule.Spec.Trigger.Interval))
|
||||
@@ -244,9 +244,9 @@ func TestIntegrationAlertRuleCompatCreateViaProvisioning(t *testing.T) {
|
||||
err = json.Unmarshal(r.Data[0].Model, &expectedModel)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, expectedModel)
|
||||
retrievedModel, ok := retrievedRule.Spec.Data["A"].Model.(map[string]interface{})
|
||||
retrievedModel, ok := retrievedRule.Spec.Expressions["A"].Model.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Data["A"].Model)
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Expressions["A"].Model)
|
||||
}
|
||||
for k, v := range expectedModel {
|
||||
require.EqualValues(t, v, retrievedModel[k], "Model field %s should match", k)
|
||||
@@ -372,13 +372,13 @@ func TestIntegrationAlertRuleCompatCreateViaProvisioningChangeGroupInK8s(t *test
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, retrievedRule)
|
||||
require.Equal(t, r.Title, retrievedRule.Spec.Title)
|
||||
require.NotNil(t, retrievedRule.Spec.Data[r.Data[0].RefID].Source)
|
||||
require.True(t, *retrievedRule.Spec.Data[r.Data[0].RefID].Source)
|
||||
require.NotNil(t, retrievedRule.Spec.Expressions[r.Data[0].RefID].Source)
|
||||
require.True(t, *retrievedRule.Spec.Expressions[r.Data[0].RefID].Source)
|
||||
require.Equal(t, r.FolderUID, retrievedRule.Annotations["grafana.app/folder"])
|
||||
require.Equal(t, created.Title, retrievedRule.Labels[v0alpha1.GroupLabelKey])
|
||||
require.Equal(t, fmt.Sprintf("%d", i), retrievedRule.Labels[v0alpha1.GroupIndexLabelKey])
|
||||
require.Equal(t, ngmodels.ProvenanceAPI, ngmodels.Provenance(retrievedRule.GetProvenanceStatus()))
|
||||
require.EqualValues(t, r.Data[0].DatasourceUID, retrievedRule.Spec.Data["X"].DatasourceUID)
|
||||
require.EqualValues(t, r.Data[0].DatasourceUID, *retrievedRule.Spec.Expressions["X"].DatasourceUID)
|
||||
expectedDuration, err := prom_model.ParseDuration(fmt.Sprintf("%ds", created.Interval))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedDuration.String(), string(retrievedRule.Spec.Trigger.Interval))
|
||||
@@ -386,9 +386,9 @@ func TestIntegrationAlertRuleCompatCreateViaProvisioningChangeGroupInK8s(t *test
|
||||
err = json.Unmarshal(r.Data[0].Model, &expectedModel)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, expectedModel)
|
||||
retrievedModel, ok := retrievedRule.Spec.Data["X"].Model.(map[string]interface{})
|
||||
retrievedModel, ok := retrievedRule.Spec.Expressions["X"].Model.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Data["X"].Model)
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Expressions["X"].Model)
|
||||
}
|
||||
for k, v := range expectedModel {
|
||||
require.EqualValues(t, v, retrievedModel[k], "Model field %s should match", k)
|
||||
|
||||
@@ -64,10 +64,10 @@ func TestIntegrationRecordingRuleCompatCreateViaK8s(t *testing.T) {
|
||||
Spec: v0alpha1.RecordingRuleSpec{
|
||||
Title: rule.Title,
|
||||
Metric: rule.Record.Metric,
|
||||
Data: map[string]v0alpha1.RecordingRuleQuery{
|
||||
Expressions: v0alpha1.RecordingRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{
|
||||
@@ -98,9 +98,9 @@ func TestIntegrationRecordingRuleCompatCreateViaK8s(t *testing.T) {
|
||||
err := json.Unmarshal(retrievedRule.Data[0].Model, &model)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, model)
|
||||
expectedModel, ok := created.Spec.Data["A"].Model.(map[string]interface{})
|
||||
expectedModel, ok := created.Spec.Expressions["A"].Model.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", created.Spec.Data["A"].Model)
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", created.Spec.Expressions["A"].Model)
|
||||
}
|
||||
for k, v := range expectedModel {
|
||||
require.EqualValues(t, v, model[k], "Model field %s should match", k)
|
||||
@@ -247,7 +247,7 @@ func TestIntegrationRecordingRuleCompatCreateViaProvisioning(t *testing.T) {
|
||||
require.Equal(t, created.Title, retrievedRule.Labels[v0alpha1.GroupLabelKey])
|
||||
require.Equal(t, fmt.Sprintf("%d", i), retrievedRule.Labels[v0alpha1.GroupIndexLabelKey])
|
||||
require.Equal(t, ngmodels.ProvenanceAPI, ngmodels.Provenance(retrievedRule.GetProvenanceStatus()))
|
||||
require.EqualValues(t, r.Data[0].DatasourceUID, retrievedRule.Spec.Data["A"].DatasourceUID)
|
||||
require.EqualValues(t, r.Data[0].DatasourceUID, *retrievedRule.Spec.Expressions["A"].DatasourceUID)
|
||||
expectedDuration, err := prom_model.ParseDuration(fmt.Sprintf("%ds", created.Interval))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedDuration.String(), string(retrievedRule.Spec.Trigger.Interval))
|
||||
@@ -255,9 +255,9 @@ func TestIntegrationRecordingRuleCompatCreateViaProvisioning(t *testing.T) {
|
||||
err = json.Unmarshal(r.Data[0].Model, &expectedModel)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, expectedModel)
|
||||
retrievedModel, ok := retrievedRule.Spec.Data["A"].Model.(map[string]interface{})
|
||||
retrievedModel, ok := retrievedRule.Spec.Expressions["A"].Model.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Data["A"].Model)
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Expressions["A"].Model)
|
||||
}
|
||||
for k, v := range expectedModel {
|
||||
require.EqualValues(t, v, retrievedModel[k], "Model field %s should match", k)
|
||||
@@ -391,7 +391,7 @@ func TestIntegrationRecordingRuleCompatCreateViaProvisioningChangeGroupInK8s(t *
|
||||
require.Equal(t, created.Title, retrievedRule.Labels[v0alpha1.GroupLabelKey])
|
||||
require.Equal(t, fmt.Sprintf("%d", i), retrievedRule.Labels[v0alpha1.GroupIndexLabelKey])
|
||||
require.Equal(t, ngmodels.ProvenanceAPI, ngmodels.Provenance(retrievedRule.GetProvenanceStatus()))
|
||||
require.EqualValues(t, r.Data[0].DatasourceUID, retrievedRule.Spec.Data["X"].DatasourceUID)
|
||||
require.EqualValues(t, r.Data[0].DatasourceUID, *retrievedRule.Spec.Expressions["X"].DatasourceUID)
|
||||
expectedDuration, err := prom_model.ParseDuration(fmt.Sprintf("%ds", created.Interval))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedDuration.String(), string(retrievedRule.Spec.Trigger.Interval))
|
||||
@@ -399,9 +399,9 @@ func TestIntegrationRecordingRuleCompatCreateViaProvisioningChangeGroupInK8s(t *
|
||||
err = json.Unmarshal(r.Data[0].Model, &expectedModel)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, expectedModel)
|
||||
retrievedModel, ok := retrievedRule.Spec.Data["X"].Model.(map[string]interface{})
|
||||
retrievedModel, ok := retrievedRule.Spec.Expressions["X"].Model.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Data["X"].Model)
|
||||
t.Fatalf("Expected model to be a map[string]interface{}, got %T", retrievedRule.Spec.Expressions["X"].Model)
|
||||
}
|
||||
for k, v := range expectedModel {
|
||||
require.EqualValues(t, v, retrievedModel[k], "Model field %s should match", k)
|
||||
|
||||
@@ -55,10 +55,10 @@ func TestIntegrationResourceIdentifier(t *testing.T) {
|
||||
Spec: v0alpha1.RecordingRuleSpec{
|
||||
Title: rule.Title,
|
||||
Metric: rule.Record.Metric,
|
||||
Data: map[string]v0alpha1.RecordingRuleQuery{
|
||||
Expressions: v0alpha1.RecordingRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{
|
||||
@@ -155,10 +155,10 @@ func TestIntegrationAccessControl(t *testing.T) {
|
||||
Spec: v0alpha1.RecordingRuleSpec{
|
||||
Title: rule.Title,
|
||||
Metric: rule.Record.Metric,
|
||||
Data: map[string]v0alpha1.RecordingRuleQuery{
|
||||
Expressions: v0alpha1.RecordingRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{
|
||||
@@ -242,10 +242,10 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
Spec: v0alpha1.RecordingRuleSpec{
|
||||
Title: rule.Title,
|
||||
Metric: rule.Record.Metric,
|
||||
Data: map[string]v0alpha1.RecordingRuleQuery{
|
||||
Expressions: v0alpha1.RecordingRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{
|
||||
@@ -294,10 +294,10 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
Spec: v0alpha1.RecordingRuleSpec{
|
||||
Title: rule.Title,
|
||||
Metric: rule.Record.Metric,
|
||||
Data: map[string]v0alpha1.RecordingRuleQuery{
|
||||
Expressions: v0alpha1.RecordingRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{
|
||||
@@ -325,8 +325,8 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Spec: v0alpha1.RecordingRuleSpec{
|
||||
Title: "invalid-recording-rule",
|
||||
Data: map[string]v0alpha1.RecordingRuleQuery{}, // Empty data should fail
|
||||
Title: "invalid-recording-rule",
|
||||
Expressions: v0alpha1.RecordingRuleExpressionMap{}, // Empty data should fail
|
||||
Trigger: v0alpha1.RecordingRuleIntervalTrigger{
|
||||
Interval: "30s",
|
||||
},
|
||||
@@ -352,10 +352,10 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
Spec: v0alpha1.RecordingRuleSpec{
|
||||
Title: rule.Title,
|
||||
Metric: rule.Record.Metric,
|
||||
Data: map[string]v0alpha1.RecordingRuleQuery{
|
||||
Expressions: v0alpha1.RecordingRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{
|
||||
@@ -404,10 +404,10 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
Spec: v0alpha1.RecordingRuleSpec{
|
||||
Title: rule.Title,
|
||||
Metric: rule.Record.Metric,
|
||||
Data: map[string]v0alpha1.RecordingRuleQuery{
|
||||
Expressions: v0alpha1.RecordingRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{
|
||||
From: v0alpha1.RecordingRulePromDurationWMillis("5m"),
|
||||
@@ -440,10 +440,10 @@ func TestIntegrationCRUD(t *testing.T) {
|
||||
Spec: v0alpha1.RecordingRuleSpec{
|
||||
Title: rule.Title,
|
||||
Metric: rule.Record.Metric,
|
||||
Data: map[string]v0alpha1.RecordingRuleQuery{
|
||||
Expressions: v0alpha1.RecordingRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{
|
||||
@@ -496,10 +496,10 @@ func TestIntegrationPatch(t *testing.T) {
|
||||
Spec: v0alpha1.RecordingRuleSpec{
|
||||
Title: rule.Title,
|
||||
Metric: rule.Record.Metric,
|
||||
Data: map[string]v0alpha1.RecordingRuleQuery{
|
||||
Expressions: v0alpha1.RecordingRuleExpressionMap{
|
||||
"A": {
|
||||
QueryType: "query",
|
||||
DatasourceUID: v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID),
|
||||
QueryType: util.Pointer(rule.Data[0].QueryType),
|
||||
DatasourceUID: util.Pointer(v0alpha1.RecordingRuleDatasourceUID(rule.Data[0].DatasourceUID)),
|
||||
Model: rule.Data[0].Model,
|
||||
Source: util.Pointer(true),
|
||||
RelativeTimeRange: &v0alpha1.RecordingRuleRelativeTimeRange{
|
||||
|
||||
@@ -2369,10 +2369,10 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"data",
|
||||
"trigger",
|
||||
"noDataState",
|
||||
"execErrState"
|
||||
"execErrState",
|
||||
"expressions"
|
||||
],
|
||||
"properties": {
|
||||
"annotations": {
|
||||
@@ -2381,22 +2381,32 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"execErrState": {
|
||||
"type": "string",
|
||||
"default": "Error",
|
||||
"enum": [
|
||||
"Error",
|
||||
"Ok",
|
||||
"Alerting",
|
||||
"KeepLast"
|
||||
]
|
||||
},
|
||||
"expressions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"queryType",
|
||||
"datasourceUID",
|
||||
"model"
|
||||
],
|
||||
"properties": {
|
||||
"datasourceUID": {
|
||||
"description": "The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource",
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9_-]+$"
|
||||
},
|
||||
"model": {},
|
||||
"queryType": {
|
||||
"description": "The type of query if this is a query expression",
|
||||
"type": "string"
|
||||
},
|
||||
"relativeTimeRange": {
|
||||
@@ -2417,21 +2427,12 @@
|
||||
}
|
||||
},
|
||||
"source": {
|
||||
"description": "Used to mark the expression to be used as the final source for the rule evaluation\nOnly one expression in a rule can be marked as the source\nFor AlertRules, this is the expression that will be evaluated against the alerting condition\nFor RecordingRules, this is the expression that will be recorded",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"execErrState": {
|
||||
"type": "string",
|
||||
"default": "Error",
|
||||
"enum": [
|
||||
"Error",
|
||||
"Ok",
|
||||
"Alerting",
|
||||
"KeepLast"
|
||||
]
|
||||
},
|
||||
"for": {
|
||||
"type": "string",
|
||||
"allOf": [
|
||||
@@ -2730,28 +2731,28 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"title",
|
||||
"data",
|
||||
"trigger",
|
||||
"metric",
|
||||
"expressions",
|
||||
"targetDatasourceUID"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"expressions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"queryType",
|
||||
"datasourceUID",
|
||||
"model"
|
||||
],
|
||||
"properties": {
|
||||
"datasourceUID": {
|
||||
"description": "The UID of the datasource to run this expression against. If omitted, the expression will be run against the `__expr__` datasource",
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z0-9_-]+$"
|
||||
},
|
||||
"model": {},
|
||||
"queryType": {
|
||||
"description": "The type of query if this is a query expression",
|
||||
"type": "string"
|
||||
},
|
||||
"relativeTimeRange": {
|
||||
@@ -2772,6 +2773,7 @@
|
||||
}
|
||||
},
|
||||
"source": {
|
||||
"description": "Used to mark the expression to be used as the final source for the rule evaluation\nOnly one expression in a rule can be marked as the source\nFor AlertRules, this is the expression that will be evaluated against the alerting condition\nFor RecordingRules, this is the expression that will be recorded",
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user