feat(observability): continue attribute for instance -> alert config (#993)
This commit is contained in:
parent
07f6c5fbce
commit
f0438e888c
6 changed files with 45 additions and 11 deletions
|
|
@ -145,6 +145,7 @@ Read-Only:
|
|||
|
||||
Read-Only:
|
||||
|
||||
- `continue` (Boolean) Whether an alert should continue matching subsequent sibling nodes.
|
||||
- `group_by` (List of String) The labels by which incoming alerts are grouped together. For example, multiple alerts coming in for cluster=A and alertname=LatencyHigh would be batched into a single group. To aggregate by all possible labels use the special value '...' as the sole label name, for example: group_by: ['...']. This effectively disables aggregation entirely, passing through all alerts as-is. This is unlikely to be what you want, unless you have a very low alert volume or your upstream notification system performs its own grouping.
|
||||
- `group_interval` (String) How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.)
|
||||
- `group_wait` (String) How long to initially wait to send a notification for a group of alerts. Allows to wait for an inhibiting alert to arrive or collect more initial alerts for the same group. (Usually ~0s to few minutes.)
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ Required:
|
|||
|
||||
Optional:
|
||||
|
||||
- `continue` (Boolean) Whether an alert should continue matching subsequent sibling nodes.
|
||||
- `group_by` (List of String) The labels by which incoming alerts are grouped together. For example, multiple alerts coming in for cluster=A and alertname=LatencyHigh would be batched into a single group. To aggregate by all possible labels use the special value '...' as the sole label name, for example: group_by: ['...']. This effectively disables aggregation entirely, passing through all alerts as-is. This is unlikely to be what you want, unless you have a very low alert volume or your upstream notification system performs its own grouping.
|
||||
- `group_interval` (String) How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.)
|
||||
- `group_wait` (String) How long to initially wait to send a notification for a group of alerts. Allows to wait for an inhibiting alert to arrive or collect more initial alerts for the same group. (Usually ~0s to few minutes.)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ type alertConfigModel struct {
|
|||
|
||||
var alertConfigTypes = map[string]attr.Type{
|
||||
"receivers": types.ListType{ElemType: types.ObjectType{AttrTypes: receiversTypes}},
|
||||
"route": types.ObjectType{AttrTypes: routeTypes},
|
||||
"route": types.ObjectType{AttrTypes: mainRouteTypes},
|
||||
"global": types.ObjectType{AttrTypes: globalConfigurationTypes},
|
||||
}
|
||||
|
||||
|
|
@ -130,6 +130,7 @@ type mainRouteModel struct {
|
|||
// Struct corresponding to Model.AlertConfig.route
|
||||
// This is used to map the routes between the mainRouteModel and the last level of recursion of the routes field
|
||||
type routeModelMiddle struct {
|
||||
Continue types.Bool `tfsdk:"continue"`
|
||||
GroupBy types.List `tfsdk:"group_by"`
|
||||
GroupInterval types.String `tfsdk:"group_interval"`
|
||||
GroupWait types.String `tfsdk:"group_wait"`
|
||||
|
|
@ -146,6 +147,7 @@ type routeModelMiddle struct {
|
|||
// Struct corresponding to Model.AlertConfig.route but without the recursive routes field
|
||||
// This is used to map the last level of recursion of the routes field
|
||||
type routeModelNoRoutes struct {
|
||||
Continue types.Bool `tfsdk:"continue"`
|
||||
GroupBy types.List `tfsdk:"group_by"`
|
||||
GroupInterval types.String `tfsdk:"group_interval"`
|
||||
GroupWait types.String `tfsdk:"group_wait"`
|
||||
|
|
@ -158,7 +160,7 @@ type routeModelNoRoutes struct {
|
|||
RepeatInterval types.String `tfsdk:"repeat_interval"`
|
||||
}
|
||||
|
||||
var routeTypes = map[string]attr.Type{
|
||||
var mainRouteTypes = map[string]attr.Type{
|
||||
"group_by": types.ListType{ElemType: types.StringType},
|
||||
"group_interval": types.StringType,
|
||||
"group_wait": types.StringType,
|
||||
|
|
@ -236,6 +238,7 @@ var webHooksConfigsTypes = map[string]attr.Type{
|
|||
}
|
||||
|
||||
var routeDescriptions = map[string]string{
|
||||
"continue": "Whether an alert should continue matching subsequent sibling nodes.",
|
||||
"group_by": "The labels by which incoming alerts are grouped together. For example, multiple alerts coming in for cluster=A and alertname=LatencyHigh would be batched into a single group. To aggregate by all possible labels use the special value '...' as the sole label name, for example: group_by: ['...']. This effectively disables aggregation entirely, passing through all alerts as-is. This is unlikely to be what you want, unless you have a very low alert volume or your upstream notification system performs its own grouping.",
|
||||
"group_interval": "How long to wait before sending a notification about new alerts that are added to a group of alerts for which an initial notification has already been sent. (Usually ~5m or more.)",
|
||||
"group_wait": "How long to initially wait to send a notification for a group of alerts. Allows to wait for an inhibiting alert to arrive or collect more initial alerts for the same group. (Usually ~0s to few minutes.)",
|
||||
|
|
@ -258,6 +261,7 @@ func getRouteListType() types.ObjectType {
|
|||
// The level should be lower or equal to the limit, if higher, the function will produce a stack overflow.
|
||||
func getRouteListTypeAux(level, limit int) types.ObjectType {
|
||||
attributeTypes := map[string]attr.Type{
|
||||
"continue": types.BoolType,
|
||||
"group_by": types.ListType{ElemType: types.StringType},
|
||||
"group_interval": types.StringType,
|
||||
"group_wait": types.StringType,
|
||||
|
|
@ -290,6 +294,11 @@ func getDatasourceRouteNestedObject() schema.ListNestedAttribute {
|
|||
// The level should be lower or equal to the limit, if higher, the function will produce a stack overflow.
|
||||
func getRouteNestedObjectAux(isDatasource bool, level, limit int) schema.ListNestedAttribute {
|
||||
attributesMap := map[string]schema.Attribute{
|
||||
"continue": schema.BoolAttribute{
|
||||
Description: routeDescriptions["continue"],
|
||||
Optional: !isDatasource,
|
||||
Computed: isDatasource,
|
||||
},
|
||||
"group_by": schema.ListAttribute{
|
||||
Description: routeDescriptions["group_by"],
|
||||
Optional: !isDatasource,
|
||||
|
|
@ -1549,7 +1558,7 @@ func getMockAlertConfig(ctx context.Context) (alertConfigModel, error) {
|
|||
return alertConfigModel{}, fmt.Errorf("mapping group by list: %w", core.DiagsToError(diags))
|
||||
}
|
||||
|
||||
mockRoute, diags := types.ObjectValue(routeTypes, map[string]attr.Value{
|
||||
mockRoute, diags := types.ObjectValue(mainRouteTypes, map[string]attr.Value{
|
||||
"receiver": types.StringValue("email-me"),
|
||||
"group_by": mockGroupByList,
|
||||
"group_wait": types.StringValue("30s"),
|
||||
|
|
@ -1759,17 +1768,17 @@ func mapReceiversToAttributes(ctx context.Context, respReceivers *[]observabilit
|
|||
|
||||
func mapRouteToAttributes(ctx context.Context, route *observability.Route) (attr.Value, error) {
|
||||
if route == nil {
|
||||
return types.ObjectNull(routeTypes), nil
|
||||
return types.ObjectNull(mainRouteTypes), nil
|
||||
}
|
||||
|
||||
groupByModel, diags := types.ListValueFrom(ctx, types.StringType, route.GroupBy)
|
||||
if diags.HasError() {
|
||||
return types.ObjectNull(routeTypes), fmt.Errorf("mapping group by: %w", core.DiagsToError(diags))
|
||||
return types.ObjectNull(mainRouteTypes), fmt.Errorf("mapping group by: %w", core.DiagsToError(diags))
|
||||
}
|
||||
|
||||
childRoutes, err := mapChildRoutesToAttributes(ctx, route.Routes)
|
||||
if err != nil {
|
||||
return types.ObjectNull(routeTypes), fmt.Errorf("mapping child routes: %w", err)
|
||||
return types.ObjectNull(mainRouteTypes), fmt.Errorf("mapping child routes: %w", err)
|
||||
}
|
||||
|
||||
routeMap := map[string]attr.Value{
|
||||
|
|
@ -1781,9 +1790,9 @@ func mapRouteToAttributes(ctx context.Context, route *observability.Route) (attr
|
|||
"routes": childRoutes,
|
||||
}
|
||||
|
||||
routeModel, diags := types.ObjectValue(routeTypes, routeMap)
|
||||
routeModel, diags := types.ObjectValue(mainRouteTypes, routeMap)
|
||||
if diags.HasError() {
|
||||
return types.ObjectNull(routeTypes), fmt.Errorf("converting route to TF types: %w", core.DiagsToError(diags))
|
||||
return types.ObjectNull(mainRouteTypes), fmt.Errorf("converting route to TF types: %w", core.DiagsToError(diags))
|
||||
}
|
||||
|
||||
return routeModel, nil
|
||||
|
|
@ -1822,6 +1831,7 @@ func mapChildRoutesToAttributes(ctx context.Context, routes *[]observability.Rou
|
|||
}
|
||||
|
||||
routeMap := map[string]attr.Value{
|
||||
"continue": types.BoolPointerValue(route.Continue),
|
||||
"group_by": groupByModel,
|
||||
"group_interval": types.StringPointerValue(route.GroupInterval),
|
||||
"group_wait": types.StringPointerValue(route.GroupWait),
|
||||
|
|
@ -2082,6 +2092,7 @@ func toRoutePayload(ctx context.Context, routeTF *mainRouteModel) (*observabilit
|
|||
}
|
||||
for i := range lastChildRoutes {
|
||||
childRoute := routeModelMiddle{
|
||||
Continue: lastChildRoutes[i].Continue,
|
||||
GroupBy: lastChildRoutes[i].GroupBy,
|
||||
GroupInterval: lastChildRoutes[i].GroupInterval,
|
||||
GroupWait: lastChildRoutes[i].GroupWait,
|
||||
|
|
@ -2160,6 +2171,7 @@ func toChildRoutePayload(ctx context.Context, routeTF *routeModelMiddle) (*obser
|
|||
}
|
||||
|
||||
return &observability.UpdateAlertConfigsPayloadRouteRoutesInner{
|
||||
Continue: conversion.BoolValueToPointer(routeTF.Continue),
|
||||
GroupBy: groupByPayload,
|
||||
GroupInterval: conversion.StringValueToPointer(routeTF.GroupInterval),
|
||||
GroupWait: conversion.StringValueToPointer(routeTF.GroupWait),
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ func fixtureReceiverModel(emailConfigs, opsGenieConfigs, webHooksConfigs basetyp
|
|||
}
|
||||
|
||||
func fixtureRouteModel() basetypes.ObjectValue {
|
||||
return types.ObjectValueMust(routeTypes, map[string]attr.Value{
|
||||
return types.ObjectValueMust(mainRouteTypes, map[string]attr.Value{
|
||||
"group_by": types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("label1"),
|
||||
types.StringValue("label2"),
|
||||
|
|
@ -76,6 +76,7 @@ func fixtureRouteModel() basetypes.ObjectValue {
|
|||
// "routes": types.ListNull(getRouteListType()),
|
||||
"routes": types.ListValueMust(getRouteListType(), []attr.Value{
|
||||
types.ObjectValueMust(getRouteListType().AttrTypes, map[string]attr.Value{
|
||||
"continue": types.BoolValue(false),
|
||||
"group_by": types.ListValueMust(types.StringType, []attr.Value{
|
||||
types.StringValue("label1"),
|
||||
types.StringValue("label2"),
|
||||
|
|
@ -96,7 +97,7 @@ func fixtureRouteModel() basetypes.ObjectValue {
|
|||
}
|
||||
|
||||
func fixtureNullRouteModel() basetypes.ObjectValue {
|
||||
return types.ObjectValueMust(routeTypes, map[string]attr.Value{
|
||||
return types.ObjectValueMust(mainRouteTypes, map[string]attr.Value{
|
||||
"group_by": types.ListNull(types.StringType),
|
||||
"group_interval": types.StringNull(),
|
||||
"group_wait": types.StringNull(),
|
||||
|
|
@ -174,6 +175,7 @@ func fixtureReceiverPayload(emailConfigs *[]observability.CreateAlertConfigRecei
|
|||
|
||||
func fixtureRoutePayload() *observability.UpdateAlertConfigsPayloadRoute {
|
||||
return &observability.UpdateAlertConfigsPayloadRoute{
|
||||
Continue: nil,
|
||||
GroupBy: utils.Ptr([]string{"label1", "label2"}),
|
||||
GroupInterval: utils.Ptr("1m"),
|
||||
GroupWait: utils.Ptr("1m"),
|
||||
|
|
@ -181,6 +183,7 @@ func fixtureRoutePayload() *observability.UpdateAlertConfigsPayloadRoute {
|
|||
RepeatInterval: utils.Ptr("1m"),
|
||||
Routes: &[]observability.UpdateAlertConfigsPayloadRouteRoutesInner{
|
||||
{
|
||||
Continue: utils.Ptr(false),
|
||||
GroupBy: utils.Ptr([]string{"label1", "label2"}),
|
||||
GroupInterval: utils.Ptr("1m"),
|
||||
GroupWait: utils.Ptr("1m"),
|
||||
|
|
@ -249,6 +252,7 @@ func fixtureWebHooksConfigsResponse() observability.WebHook {
|
|||
|
||||
func fixtureRouteResponse() *observability.Route {
|
||||
return &observability.Route{
|
||||
Continue: nil,
|
||||
GroupBy: utils.Ptr([]string{"label1", "label2"}),
|
||||
GroupInterval: utils.Ptr("1m"),
|
||||
GroupWait: utils.Ptr("1m"),
|
||||
|
|
@ -259,6 +263,7 @@ func fixtureRouteResponse() *observability.Route {
|
|||
RepeatInterval: utils.Ptr("1m"),
|
||||
Routes: &[]observability.RouteSerializer{
|
||||
{
|
||||
Continue: utils.Ptr(false),
|
||||
GroupBy: utils.Ptr([]string{"label1", "label2"}),
|
||||
GroupInterval: utils.Ptr("1m"),
|
||||
GroupWait: utils.Ptr("1m"),
|
||||
|
|
@ -287,6 +292,11 @@ func fixtureGlobalConfigResponse() *observability.Global {
|
|||
|
||||
func fixtureRouteAttributeSchema(route *schema.ListNestedAttribute, isDatasource bool) map[string]schema.Attribute {
|
||||
attributeMap := map[string]schema.Attribute{
|
||||
"continue": schema.BoolAttribute{
|
||||
Description: routeDescriptions["continue"],
|
||||
Optional: !isDatasource,
|
||||
Computed: isDatasource,
|
||||
},
|
||||
"group_by": schema.ListAttribute{
|
||||
Description: routeDescriptions["group_by"],
|
||||
Optional: !isDatasource,
|
||||
|
|
@ -877,7 +887,7 @@ func TestMapAlertConfigField(t *testing.T) {
|
|||
fixtureWebHooksConfigsModel(),
|
||||
),
|
||||
}),
|
||||
"route": types.ObjectNull(routeTypes),
|
||||
"route": types.ObjectNull(mainRouteTypes),
|
||||
"global": types.ObjectNull(globalConfigurationTypes),
|
||||
}),
|
||||
},
|
||||
|
|
@ -1497,6 +1507,7 @@ func TestGetRouteListTypeAux(t *testing.T) {
|
|||
1,
|
||||
types.ObjectType{
|
||||
AttrTypes: map[string]attr.Type{
|
||||
"continue": types.BoolType,
|
||||
"group_by": types.ListType{ElemType: types.StringType},
|
||||
"group_interval": types.StringType,
|
||||
"group_wait": types.StringType,
|
||||
|
|
@ -1514,6 +1525,7 @@ func TestGetRouteListTypeAux(t *testing.T) {
|
|||
2,
|
||||
types.ObjectType{
|
||||
AttrTypes: map[string]attr.Type{
|
||||
"continue": types.BoolType,
|
||||
"group_by": types.ListType{ElemType: types.StringType},
|
||||
"group_interval": types.StringType,
|
||||
"group_wait": types.StringType,
|
||||
|
|
@ -1523,6 +1535,7 @@ func TestGetRouteListTypeAux(t *testing.T) {
|
|||
"receiver": types.StringType,
|
||||
"repeat_interval": types.StringType,
|
||||
"routes": types.ListType{ElemType: types.ObjectType{AttrTypes: map[string]attr.Type{
|
||||
"continue": types.BoolType,
|
||||
"group_by": types.ListType{ElemType: types.StringType},
|
||||
"group_interval": types.StringType,
|
||||
"group_wait": types.StringType,
|
||||
|
|
@ -1541,6 +1554,7 @@ func TestGetRouteListTypeAux(t *testing.T) {
|
|||
2,
|
||||
types.ObjectType{
|
||||
AttrTypes: map[string]attr.Type{
|
||||
"continue": types.BoolType,
|
||||
"group_by": types.ListType{ElemType: types.StringType},
|
||||
"group_interval": types.StringType,
|
||||
"group_wait": types.StringType,
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ var testConfigVarsMax = config.Variables{
|
|||
"match": config.StringVariable("alert1"),
|
||||
"match_regex": config.StringVariable("alert1"),
|
||||
"matchers": config.StringVariable("instance =~ \".*\""),
|
||||
"continue": config.StringVariable("true"),
|
||||
// credential
|
||||
"credential_description": config.StringVariable("This is a description for the test credential."),
|
||||
// logalertgroup
|
||||
|
|
@ -536,6 +537,7 @@ func TestAccResourceMax(t *testing.T) {
|
|||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.group_wait", testutil.ConvertConfigVariable(testConfigVarsMax["group_wait"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.receiver", testutil.ConvertConfigVariable(testConfigVarsMax["receiver_name"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.repeat_interval", testutil.ConvertConfigVariable(testConfigVarsMax["repeat_interval"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.continue", testutil.ConvertConfigVariable(testConfigVarsMax["continue"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.match.match1", testutil.ConvertConfigVariable(testConfigVarsMax["match"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.match_regex.match_regex1", testutil.ConvertConfigVariable(testConfigVarsMax["match_regex"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.matchers.0", testutil.ConvertConfigVariable(testConfigVarsMax["matchers"])),
|
||||
|
|
@ -702,6 +704,7 @@ func TestAccResourceMax(t *testing.T) {
|
|||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.route.routes.0.group_wait", testutil.ConvertConfigVariable(testConfigVarsMax["group_wait"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.route.routes.0.receiver", testutil.ConvertConfigVariable(testConfigVarsMax["receiver_name"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.route.routes.0.repeat_interval", testutil.ConvertConfigVariable(testConfigVarsMax["repeat_interval"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.route.routes.0.continue", testutil.ConvertConfigVariable(testConfigVarsMax["continue"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.route.routes.0.match.match1", testutil.ConvertConfigVariable(testConfigVarsMax["match"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.route.routes.0.match_regex.match_regex1", testutil.ConvertConfigVariable(testConfigVarsMax["match_regex"])),
|
||||
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.route.routes.0.matchers.0", testutil.ConvertConfigVariable(testConfigVarsMax["matchers"])),
|
||||
|
|
@ -928,6 +931,7 @@ func TestAccResourceMax(t *testing.T) {
|
|||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.group_wait", testutil.ConvertConfigVariable(testConfigVarsMax["group_wait"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.receiver", testutil.ConvertConfigVariable(testConfigVarsMax["receiver_name"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.repeat_interval", testutil.ConvertConfigVariable(testConfigVarsMax["repeat_interval"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.continue", testutil.ConvertConfigVariable(testConfigVarsMax["continue"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.match.match1", testutil.ConvertConfigVariable(testConfigVarsMax["match"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.match_regex.match_regex1", testutil.ConvertConfigVariable(testConfigVarsMax["match_regex"])),
|
||||
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.routes.0.matchers.0", testutil.ConvertConfigVariable(configVarsMaxUpdated()["matchers"])),
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ variable "smtp_smart_host" {}
|
|||
variable "match" {}
|
||||
variable "match_regex" {}
|
||||
variable "matchers" {}
|
||||
variable "continue" {}
|
||||
|
||||
variable "credential_description" {}
|
||||
|
||||
|
|
@ -155,6 +156,7 @@ resource "stackit_observability_instance" "instance" {
|
|||
group_wait = var.group_wait
|
||||
receiver = var.receiver_name
|
||||
repeat_interval = var.repeat_interval
|
||||
continue = var.continue
|
||||
match = {
|
||||
match1 = var.match
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue