Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/data-sources/observability_alertgroup.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ Read-Only:
- `expression` (String) The PromQL expression to evaluate. Every evaluation cycle this is evaluated at the current time, and all resultant time series become pending/firing alerts.
- `for` (String) Alerts are considered firing once they have been returned for this long. Alerts which have not yet fired for long enough are considered pending. Default is 0s
- `labels` (Map of String) A map of key:value. Labels to add or overwrite for each alert
- `record` (String) The name of the metric. It's the identifier and must be unique in the group.
10 changes: 3 additions & 7 deletions docs/resources/observability_alertgroup.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,11 @@ resource "stackit_observability_alertgroup" "example" {
}
},
{
alert = "example-alert-name-2"
expression = "kube_node_status_condition{condition=\"Ready\", status=\"false\"} > 0"
for = "1m"
labels = {
severity = "critical"
},
annotations = {
summary : "example summary"
description : "example description"
}
record = "example_record_name"
},
]
}
Expand Down Expand Up @@ -76,11 +71,12 @@ import {

Required:

- `alert` (String) The name of the alert rule. Is the identifier and must be unique in the group.
- `expression` (String) The PromQL expression to evaluate. Every evaluation cycle this is evaluated at the current time, and all resultant time series become pending/firing alerts.

Optional:

- `alert` (String) The name of the alert rule. Is the identifier and must be unique in the group.
- `annotations` (Map of String) A map of key:value. Annotations to add or overwrite for each alert
- `for` (String) Alerts are considered firing once they have been returned for this long. Alerts which have not yet fired for long enough are considered pending. Default is 0s
- `labels` (Map of String) A map of key:value. Labels to add or overwrite for each alert
- `record` (String) The name of the metric. It's the identifier and must be unique in the group.
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,11 @@ resource "stackit_observability_alertgroup" "example" {
}
},
{
alert = "example-alert-name-2"
expression = "kube_node_status_condition{condition=\"Ready\", status=\"false\"} > 0"
for = "1m"
labels = {
severity = "critical"
},
annotations = {
summary : "example summary"
description : "example description"
}
record = "example_record_name"
},
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ func (a *alertGroupDataSource) Schema(_ context.Context, _ datasource.SchemaRequ
ElementType: types.StringType,
Computed: true,
},
"record": schema.StringAttribute{
Description: descriptions["record"],
Computed: true,
},
},
},
},
Expand Down
59 changes: 58 additions & 1 deletion stackit/internal/services/observability/alertgroup/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type rule struct {
Labels types.Map `tfsdk:"labels"`
Expression types.String `tfsdk:"expression"`
For types.String `tfsdk:"for"`
Record types.String `tfsdk:"record"`
}

var ruleTypes = map[string]attr.Type{
Expand All @@ -61,6 +62,7 @@ var ruleTypes = map[string]attr.Type{
"labels": basetypes.MapType{ElemType: types.StringType},
"expression": basetypes.StringType{},
"for": basetypes.StringType{},
"record": basetypes.StringType{},
}

// Descriptions for the resource and data source schemas are centralized here.
Expand All @@ -75,6 +77,7 @@ var descriptions = map[string]string{
"for": "Alerts are considered firing once they have been returned for this long. Alerts which have not yet fired for long enough are considered pending. Default is 0s",
"labels": "A map of key:value. Labels to add or overwrite for each alert",
"annotations": "A map of key:value. Annotations to add or overwrite for each alert",
"record": "The name of the metric. It's the identifier and must be unique in the group.",
}

// NewAlertGroupResource is a helper function to simplify the provider implementation.
Expand Down Expand Up @@ -107,6 +110,40 @@ func (a *alertGroupResource) Configure(ctx context.Context, req resource.Configu
tflog.Info(ctx, "Observability alert group client configured")
}

func (a *alertGroupResource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
var resourceModel Model
resp.Diagnostics.Append(req.Config.Get(ctx, &resourceModel)...)
if resp.Diagnostics.HasError() {
return
}

rules := &[]rule{}
diags := resourceModel.Rules.ElementsAs(ctx, rules, false)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

// check every rule if the requirements are met, if one fails the whole config fails
rs := *rules
for i := range rs {
// either `alert` or `record` is needed
if rs[i].Alert.IsNull() && rs[i].Record.IsNull() {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring alertgroup", "You need to provide either `alert` or `record` for a `rule`.")
}

// both are set, only one is allowed
if !rs[i].Alert.IsNull() && !rs[i].Record.IsNull() {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring alertgroup", "Both `alert` and `record` were set for a`rule`. Only one is allowed.")
}

// if record is set, `annotations` and `for` are not allowed
if (!rs[i].Record.IsNull() && !rs[i].Record.IsUnknown()) && ((!rs[i].Annotations.IsNull() && !rs[i].Annotations.IsUnknown()) || (!rs[i].For.IsNull() && !rs[i].For.IsUnknown())) {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring alertgroup", "Setting either `annotations` or `for` when using `record` is not allowed.")
}
}
}

// Schema defines the schema for the resource.
func (a *alertGroupResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Expand Down Expand Up @@ -174,7 +211,7 @@ func (a *alertGroupResource) Schema(_ context.Context, _ resource.SchemaRequest,
Attributes: map[string]schema.Attribute{
"alert": schema.StringAttribute{
Description: descriptions["alert"],
Required: true,
Optional: true,
Validators: []validator.String{
stringvalidator.RegexMatches(
regexp.MustCompile(`^[a-zA-Z0-9-]+$`),
Expand Down Expand Up @@ -222,6 +259,17 @@ func (a *alertGroupResource) Schema(_ context.Context, _ resource.SchemaRequest,
mapvalidator.SizeAtMost(5),
},
},
"record": schema.StringAttribute{
Description: descriptions["record"],
Optional: true,
Validators: []validator.String{
stringvalidator.RegexMatches(
regexp.MustCompile(`^[a-zA-Z0-9:_]+$`),
"must match expression",
),
stringvalidator.LengthBetween(1, 300),
},
},
},
},
},
Expand Down Expand Up @@ -468,6 +516,14 @@ func toRulesPayload(ctx context.Context, model *Model) ([]observability.UpdateAl
oarr.Annotations = &annotations
}

if !utils.IsUndefined(rule.Record) {
record := conversion.StringValueToPointer(rule.Record)
if record == nil {
return nil, fmt.Errorf("found nil record for rule[%d]", i)
}
oarr.Record = record
}

oarrs = append(oarrs, oarr)
}

Expand Down Expand Up @@ -539,6 +595,7 @@ func mapRules(_ context.Context, alertGroup *observability.AlertGroup, model *Mo
"for": types.StringPointerValue(r.For),
"labels": types.MapNull(types.StringType),
"annotations": types.MapNull(types.StringType),
"record": types.StringPointerValue(r.Record),
}

if r.Labels != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ func TestToCreatePayload(t *testing.T) {
"k": types.StringValue("v"),
},
),
"record": types.StringValue("record"),
},
),
},
Expand All @@ -91,6 +92,7 @@ func TestToCreatePayload(t *testing.T) {
Labels: &map[string]interface{}{
"k": "v",
},
Record: utils.Ptr("record"),
},
},
},
Expand Down Expand Up @@ -153,6 +155,7 @@ func TestToRulesPayload(t *testing.T) {
"annotations": types.MapValueMust(types.StringType, map[string]attr.Value{
"note": types.StringValue("important"),
}),
"record": types.StringValue("record"),
}),
}),
},
Expand All @@ -167,6 +170,7 @@ func TestToRulesPayload(t *testing.T) {
Annotations: &map[string]interface{}{
"note": "important",
},
Record: utils.Ptr("record"),
},
},
expectErr: false,
Expand All @@ -181,6 +185,7 @@ func TestToRulesPayload(t *testing.T) {
"for": types.StringValue("5s"),
"labels": types.MapNull(types.StringType),
"annotations": types.MapNull(types.StringType),
"record": types.StringValue("record1"),
}),
types.ObjectValueMust(ruleTypes, map[string]attr.Value{
"alert": types.StringValue("alert2"),
Expand All @@ -192,14 +197,16 @@ func TestToRulesPayload(t *testing.T) {
"annotations": types.MapValueMust(types.StringType, map[string]attr.Value{
"note": types.StringValue("important"),
}),
"record": types.StringValue("record2"),
}),
}),
},
expect: []observability.UpdateAlertgroupsRequestInnerRulesInner{
{
Alert: utils.Ptr("alert1"),
Expr: utils.Ptr("expr1"),
For: utils.Ptr("5s"),
Alert: utils.Ptr("alert1"),
Expr: utils.Ptr("expr1"),
For: utils.Ptr("5s"),
Record: utils.Ptr("record1"),
},
{
Alert: utils.Ptr("alert2"),
Expand All @@ -211,6 +218,7 @@ func TestToRulesPayload(t *testing.T) {
Annotations: &map[string]interface{}{
"note": "important",
},
Record: utils.Ptr("record2"),
},
},
expectErr: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ var testConfigVarsMin = config.Variables{
"alertgroup_name": config.StringVariable(fmt.Sprintf("tf-acc-ag%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
"alert_rule_name": config.StringVariable("alert1"),
"alert_rule_expression": config.StringVariable(alert_rule_expression),
"record_rule_name": config.StringVariable("record1"),
"instance_name": config.StringVariable(fmt.Sprintf("tf-acc-i%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
"plan_name": config.StringVariable("Observability-Medium-EU01"),
"logalertgroup_name": config.StringVariable(fmt.Sprintf("tf-acc-lag%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
Expand All @@ -55,6 +56,7 @@ var testConfigVarsMax = config.Variables{
"alertgroup_name": config.StringVariable(fmt.Sprintf("tf-acc-ag%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
"alert_rule_name": config.StringVariable("alert1"),
"alert_rule_expression": config.StringVariable(alert_rule_expression),
"record_rule_name": config.StringVariable("record1"),
"instance_name": config.StringVariable(fmt.Sprintf("tf-acc-i%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
"plan_name": config.StringVariable("Observability-Medium-EU01"),
"logalertgroup_name": config.StringVariable(fmt.Sprintf("tf-acc-lag%s", acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum))),
Expand Down Expand Up @@ -215,6 +217,8 @@ func TestAccResourceMin(t *testing.T) {
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "name", testutil.ConvertConfigVariable(testConfigVarsMin["alertgroup_name"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.alert", testutil.ConvertConfigVariable(testConfigVarsMin["alert_rule_name"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.expression", alert_rule_expression),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.1.record", testutil.ConvertConfigVariable(testConfigVarsMin["record_rule_name"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.1.expression", alert_rule_expression),

// logalertgroup
resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
Expand Down Expand Up @@ -302,6 +306,8 @@ func TestAccResourceMin(t *testing.T) {
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "name", testutil.ConvertConfigVariable(testConfigVarsMin["alertgroup_name"])),
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "rules.0.alert", testutil.ConvertConfigVariable(testConfigVarsMin["alert_rule_name"])),
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "rules.0.expression", alert_rule_expression),
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "rules.1.record", testutil.ConvertConfigVariable(testConfigVarsMin["record_rule_name"])),
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "rules.1.expression", alert_rule_expression),

// logalertgroup
resource.TestCheckResourceAttr("data.stackit_observability_logalertgroup.logalertgroup", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
Expand Down Expand Up @@ -460,6 +466,8 @@ func TestAccResourceMin(t *testing.T) {
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "name", testutil.ConvertConfigVariable(testConfigVarsMin["alertgroup_name"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.alert", testutil.ConvertConfigVariable(configVarsMinUpdated()["alert_rule_name"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.expression", alert_rule_expression),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.1.record", testutil.ConvertConfigVariable(configVarsMinUpdated()["record_rule_name"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.1.expression", alert_rule_expression),

// logalertgroup
resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
Expand Down Expand Up @@ -606,6 +614,9 @@ func TestAccResourceMax(t *testing.T) {
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.expression", alert_rule_expression),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.for", testutil.ConvertConfigVariable(testConfigVarsMax["alert_for_time"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.labels.label1", testutil.ConvertConfigVariable(testConfigVarsMax["alert_label"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.1.record", testutil.ConvertConfigVariable(testConfigVarsMax["record_rule_name"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.1.expression", alert_rule_expression),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.1.labels.label1", testutil.ConvertConfigVariable(testConfigVarsMax["alert_label"])),

resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.annotations.annotation1", testutil.ConvertConfigVariable(testConfigVarsMax["alert_annotation"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "interval", testutil.ConvertConfigVariable(testConfigVarsMax["alert_interval"])),
Expand Down Expand Up @@ -776,6 +787,9 @@ func TestAccResourceMax(t *testing.T) {
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "rules.0.labels.label1", testutil.ConvertConfigVariable(testConfigVarsMax["alert_label"])),
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "rules.0.annotations.annotation1", testutil.ConvertConfigVariable(testConfigVarsMax["alert_annotation"])),
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "interval", testutil.ConvertConfigVariable(testConfigVarsMax["alert_interval"])),
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "rules.1.record", testutil.ConvertConfigVariable(testConfigVarsMax["record_rule_name"])),
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "rules.1.expression", alert_rule_expression),
resource.TestCheckResourceAttr("data.stackit_observability_alertgroup.alertgroup", "rules.1.labels.label1", testutil.ConvertConfigVariable(testConfigVarsMax["alert_label"])),

// logalertgroup
resource.TestCheckResourceAttr("stackit_observability_logalertgroup.logalertgroup", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])),
Expand Down Expand Up @@ -1006,6 +1020,9 @@ func TestAccResourceMax(t *testing.T) {
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.expression", alert_rule_expression_updated),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.for", testutil.ConvertConfigVariable(testConfigVarsMax["alert_for_time"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.labels.label1", testutil.ConvertConfigVariable(testConfigVarsMax["alert_label"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.1.record", testutil.ConvertConfigVariable(testConfigVarsMax["record_rule_name"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.1.expression", alert_rule_expression_updated),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.1.labels.label1", testutil.ConvertConfigVariable(testConfigVarsMax["alert_label"])),

resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "rules.0.annotations.annotation1", testutil.ConvertConfigVariable(testConfigVarsMax["alert_annotation"])),
resource.TestCheckResourceAttr("stackit_observability_alertgroup.alertgroup", "interval", testutil.ConvertConfigVariable(configVarsMaxUpdated()["alert_interval"])),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ variable "alert_for_time" {}
variable "alert_label" {}
variable "alert_annotation" {}
variable "alert_interval" {}
variable "record_rule_name" {}

variable "instance_name" {}
variable "plan_name" {}
Expand Down Expand Up @@ -89,6 +90,13 @@ resource "stackit_observability_alertgroup" "alertgroup" {
annotations = {
annotation1 = var.alert_annotation
}
},
{
record = var.record_rule_name
expression = var.alert_rule_expression
labels = {
label1 = var.alert_label
}
}
]
interval = var.alert_interval
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ variable "project_id" {}
variable "alertgroup_name" {}
variable "alert_rule_name" {}
variable "alert_rule_expression" {}
variable "record_rule_name" {}

variable "instance_name" {}
variable "plan_name" {}
Expand All @@ -27,6 +28,10 @@ resource "stackit_observability_alertgroup" "alertgroup" {
{
alert = var.alert_rule_name
expression = var.alert_rule_expression
},
{
record = var.record_rule_name
expression = var.alert_rule_expression
}
]
}
Expand Down
Loading