diff --git a/docs/data-sources/observability_instance.md b/docs/data-sources/observability_instance.md index 0f70fe8c2..62cbb2762 100644 --- a/docs/data-sources/observability_instance.md +++ b/docs/data-sources/observability_instance.md @@ -131,8 +131,6 @@ Read-Only: - `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.) . -- `match` (Map of String) A set of equality matchers an alert has to fulfill to match the node. -- `match_regex` (Map of String) A set of regex-matchers an alert has to fulfill to match the node. - `receiver` (String) The name of the receiver to route the alerts to. - `repeat_interval` (String) How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more). - `routes` (Attributes List) List of child routes. (see [below for nested schema](#nestedatt--alert_config--route--routes)) @@ -145,7 +143,8 @@ Read-Only: - `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.) -- `match` (Map of String) A set of equality matchers an alert has to fulfill to match the node. -- `match_regex` (Map of String) A set of regex-matchers an alert has to fulfill to match the node. +- `match` (Map of String, Deprecated) A set of equality matchers an alert has to fulfill to match the node. This field is deprecated and will be removed after 16th January 2026, use `matchers` in the `routes` instead +- `match_regex` (Map of String, Deprecated) A set of regex-matchers an alert has to fulfill to match the node. This field is deprecated and will be removed after 16th January 2026, use `matchers` in the `routes` instead +- `matchers` (List of String) A list of matchers that an alert has to fulfill to match the node. A matcher is a string with a syntax inspired by PromQL and OpenMetrics. - `receiver` (String) The name of the receiver to route the alerts to. - `repeat_interval` (String) How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more). diff --git a/docs/resources/observability_instance.md b/docs/resources/observability_instance.md index 3c8cebbd5..65943df17 100644 --- a/docs/resources/observability_instance.md +++ b/docs/resources/observability_instance.md @@ -134,8 +134,6 @@ Optional: - `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.) -- `match` (Map of String) A set of equality matchers an alert has to fulfill to match the node. -- `match_regex` (Map of String) A set of regex-matchers an alert has to fulfill to match the node. - `repeat_interval` (String) How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more). - `routes` (Attributes List) List of child routes. (see [below for nested schema](#nestedatt--alert_config--route--routes)) @@ -151,8 +149,9 @@ Optional: - `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.) -- `match` (Map of String) A set of equality matchers an alert has to fulfill to match the node. -- `match_regex` (Map of String) A set of regex-matchers an alert has to fulfill to match the node. +- `match` (Map of String, Deprecated) A set of equality matchers an alert has to fulfill to match the node. This field is deprecated and will be removed after 16th January 2026, use `matchers` in the `routes` instead +- `match_regex` (Map of String, Deprecated) A set of regex-matchers an alert has to fulfill to match the node. This field is deprecated and will be removed after 16th January 2026, use `matchers` in the `routes` instead +- `matchers` (List of String) A list of matchers that an alert has to fulfill to match the node. A matcher is a string with a syntax inspired by PromQL and OpenMetrics. - `repeat_interval` (String) How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more). diff --git a/stackit/internal/services/observability/instance/datasource.go b/stackit/internal/services/observability/instance/datasource.go index d55a4d292..593675763 100644 --- a/stackit/internal/services/observability/instance/datasource.go +++ b/stackit/internal/services/observability/instance/datasource.go @@ -286,16 +286,6 @@ func (d *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques Description: "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.) .", Computed: true, }, - "match": schema.MapAttribute{ - Description: "A set of equality matchers an alert has to fulfill to match the node.", - Computed: true, - ElementType: types.StringType, - }, - "match_regex": schema.MapAttribute{ - Description: "A set of regex-matchers an alert has to fulfill to match the node.", - Computed: true, - ElementType: types.StringType, - }, "receiver": schema.StringAttribute{ Description: "The name of the receiver to route the alerts to.", Computed: true, diff --git a/stackit/internal/services/observability/instance/resource.go b/stackit/internal/services/observability/instance/resource.go index 36b7e4d85..b4e43d81b 100644 --- a/stackit/internal/services/observability/instance/resource.go +++ b/stackit/internal/services/observability/instance/resource.go @@ -117,12 +117,26 @@ var globalConfigurationTypes = map[string]attr.Type{ } // Struct corresponding to Model.AlertConfig.route -type routeModel struct { +type mainRouteModel struct { GroupBy types.List `tfsdk:"group_by"` GroupInterval types.String `tfsdk:"group_interval"` GroupWait types.String `tfsdk:"group_wait"` - Match types.Map `tfsdk:"match"` + Receiver types.String `tfsdk:"receiver"` + RepeatInterval types.String `tfsdk:"repeat_interval"` + Routes types.List `tfsdk:"routes"` +} + +// 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 { + GroupBy types.List `tfsdk:"group_by"` + GroupInterval types.String `tfsdk:"group_interval"` + GroupWait types.String `tfsdk:"group_wait"` + // Deprecated: Match is deprecated and will be removed after 16th January 2026. Use Matchers instead + Match types.Map `tfsdk:"match"` + // Deprecated: MatchRegex is deprecated and will be removed after 16th January 2026. Use Matchers instead MatchRegex types.Map `tfsdk:"match_regex"` + Matchers types.List `tfsdk:"matchers"` Receiver types.String `tfsdk:"receiver"` RepeatInterval types.String `tfsdk:"repeat_interval"` Routes types.List `tfsdk:"routes"` @@ -131,11 +145,14 @@ type routeModel 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 { - GroupBy types.List `tfsdk:"group_by"` - GroupInterval types.String `tfsdk:"group_interval"` - GroupWait types.String `tfsdk:"group_wait"` - Match types.Map `tfsdk:"match"` + GroupBy types.List `tfsdk:"group_by"` + GroupInterval types.String `tfsdk:"group_interval"` + GroupWait types.String `tfsdk:"group_wait"` + // Deprecated: Match is deprecated and will be removed after 16th January 2026. Use Matchers instead + Match types.Map `tfsdk:"match"` + // Deprecated: MatchRegex is deprecated and will be removed after 16th January 2026. Use Matchers instead MatchRegex types.Map `tfsdk:"match_regex"` + Matchers types.List `tfsdk:"matchers"` Receiver types.String `tfsdk:"receiver"` RepeatInterval types.String `tfsdk:"repeat_interval"` } @@ -144,8 +161,6 @@ var routeTypes = map[string]attr.Type{ "group_by": types.ListType{ElemType: types.StringType}, "group_interval": types.StringType, "group_wait": types.StringType, - "match": types.MapType{ElemType: types.StringType}, - "match_regex": types.MapType{ElemType: types.StringType}, "receiver": types.StringType, "repeat_interval": types.StringType, "routes": types.ListType{ElemType: getRouteListType()}, @@ -213,8 +228,9 @@ var routeDescriptions = map[string]string{ "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.)", - "match": "A set of equality matchers an alert has to fulfill to match the node.", - "match_regex": "A set of regex-matchers an alert has to fulfill to match the node.", + "match": "A set of equality matchers an alert has to fulfill to match the node. This field is deprecated and will be removed after 16th January 2026, use `matchers` in the `routes` instead", + "match_regex": "A set of regex-matchers an alert has to fulfill to match the node. This field is deprecated and will be removed after 16th January 2026, use `matchers` in the `routes` instead", + "matchers": "A list of matchers that an alert has to fulfill to match the node. A matcher is a string with a syntax inspired by PromQL and OpenMetrics.", "receiver": "The name of the receiver to route the alerts to.", "repeat_interval": "How long to wait before sending a notification again if it has already been sent successfully for an alert. (Usually ~3h or more).", "routes": "List of child routes.", @@ -236,6 +252,7 @@ func getRouteListTypeAux(level, limit int) types.ObjectType { "group_wait": types.StringType, "match": types.MapType{ElemType: types.StringType}, "match_regex": types.MapType{ElemType: types.StringType}, + "matchers": types.ListType{ElemType: types.StringType}, "receiver": types.StringType, "repeat_interval": types.StringType, } @@ -285,13 +302,21 @@ func getRouteNestedObjectAux(isDatasource bool, level, limit int) schema.ListNes }, }, "match": schema.MapAttribute{ - Description: routeDescriptions["match"], - Optional: !isDatasource, - Computed: isDatasource, - ElementType: types.StringType, + Description: routeDescriptions["match"], + DeprecationMessage: "Use `matchers` in the `routes` instead.", + Optional: !isDatasource, + Computed: isDatasource, + ElementType: types.StringType, }, "match_regex": schema.MapAttribute{ - Description: routeDescriptions["match_regex"], + Description: routeDescriptions["match_regex"], + DeprecationMessage: "Use `matchers` in the `routes` instead.", + Optional: !isDatasource, + Computed: isDatasource, + ElementType: types.StringType, + }, + "matchers": schema.ListAttribute{ + Description: routeDescriptions["matchers"], Optional: !isDatasource, Computed: isDatasource, ElementType: types.StringType, @@ -679,16 +704,6 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r stringplanmodifier.UseStateForUnknown(), }, }, - "match": schema.MapAttribute{ - Description: routeDescriptions["match"], - Optional: true, - ElementType: types.StringType, - }, - "match_regex": schema.MapAttribute{ - Description: routeDescriptions["match_regex"], - Optional: true, - ElementType: types.StringType, - }, "receiver": schema.StringAttribute{ Description: routeDescriptions["receiver"], Required: true, @@ -1549,8 +1564,6 @@ func getMockAlertConfig(ctx context.Context) (alertConfigModel, error) { "group_wait": types.StringValue("30s"), "group_interval": types.StringValue("5m"), "repeat_interval": types.StringValue("4h"), - "match": types.MapNull(types.StringType), - "match_regex": types.MapNull(types.StringType), "routes": types.ListNull(getRouteListType()), }) if diags.HasError() { @@ -1758,16 +1771,6 @@ func mapRouteToAttributes(ctx context.Context, route *observability.Route) (attr return types.ObjectNull(routeTypes), fmt.Errorf("mapping group by: %w", core.DiagsToError(diags)) } - matchModel, diags := types.MapValueFrom(ctx, types.StringType, route.Match) - if diags.HasError() { - return types.ObjectNull(routeTypes), fmt.Errorf("mapping match: %w", core.DiagsToError(diags)) - } - - matchRegexModel, diags := types.MapValueFrom(ctx, types.StringType, route.MatchRe) - if diags.HasError() { - return types.ObjectNull(routeTypes), fmt.Errorf("mapping match regex: %w", core.DiagsToError(diags)) - } - childRoutes, err := mapChildRoutesToAttributes(ctx, route.Routes) if err != nil { return types.ObjectNull(routeTypes), fmt.Errorf("mapping child routes: %w", err) @@ -1777,8 +1780,6 @@ func mapRouteToAttributes(ctx context.Context, route *observability.Route) (attr "group_by": groupByModel, "group_interval": types.StringPointerValue(route.GroupInterval), "group_wait": types.StringPointerValue(route.GroupWait), - "match": matchModel, - "match_regex": matchRegexModel, "receiver": types.StringPointerValue(route.Receiver), "repeat_interval": types.StringPointerValue(route.RepeatInterval), "routes": childRoutes, @@ -1819,12 +1820,18 @@ func mapChildRoutesToAttributes(ctx context.Context, routes *[]observability.Rou return nullList, fmt.Errorf("mapping match regex: %w", core.DiagsToError(diags)) } + matchersModel, diags := types.ListValueFrom(ctx, types.StringType, route.Matchers) + if diags.HasError() { + return nullList, fmt.Errorf("mapping matchers: %w", core.DiagsToError(diags)) + } + routeMap := map[string]attr.Value{ "group_by": groupByModel, "group_interval": types.StringPointerValue(route.GroupInterval), "group_wait": types.StringPointerValue(route.GroupWait), "match": matchModel, "match_regex": matchRegexModel, + "matchers": matchersModel, "receiver": types.StringPointerValue(route.Receiver), "repeat_interval": types.StringPointerValue(route.RepeatInterval), } @@ -1944,7 +1951,7 @@ func toUpdateAlertConfigPayload(ctx context.Context, model *alertConfigModel) (* return nil, fmt.Errorf("mapping receivers: %w", err) } - routeTF := routeModel{} + routeTF := mainRouteModel{} diags := model.Route.As(ctx, &routeTF, basetypes.ObjectAsOptions{}) if diags.HasError() { return nil, fmt.Errorf("mapping route: %w", core.DiagsToError(diags)) @@ -2044,7 +2051,74 @@ func toReceiverPayload(ctx context.Context, model *alertConfigModel) (*[]observa return &receivers, nil } -func toRoutePayload(ctx context.Context, routeTF *routeModel) (*observability.UpdateAlertConfigsPayloadRoute, error) { +func toRoutePayload(ctx context.Context, routeTF *mainRouteModel) (*observability.UpdateAlertConfigsPayloadRoute, error) { + if routeTF == nil { + return nil, fmt.Errorf("nil route model") + } + + var groupByPayload *[]string + var childRoutesPayload *[]observability.CreateAlertConfigRoutePayloadRoutesInner + + if !routeTF.GroupBy.IsNull() && !routeTF.GroupBy.IsUnknown() { + groupByPayload = &[]string{} + diags := routeTF.GroupBy.ElementsAs(ctx, groupByPayload, false) + if diags.HasError() { + return nil, fmt.Errorf("mapping group by: %w", core.DiagsToError(diags)) + } + } + + if !routeTF.Routes.IsNull() && !routeTF.Routes.IsUnknown() { + childRoutes := []routeModelMiddle{} + diags := routeTF.Routes.ElementsAs(ctx, &childRoutes, false) + if diags.HasError() { + // If there is an error, we will try to map the child routes as if they are the last child routes + // This is done because the last child routes in the recursion have a different structure (don't have the `routes` fields) + // and need to be unpacked to a different struct (routeModelNoRoutes) + lastChildRoutes := []routeModelNoRoutes{} + diags = routeTF.Routes.ElementsAs(ctx, &lastChildRoutes, true) + if diags.HasError() { + return nil, fmt.Errorf("mapping child routes: %w", core.DiagsToError(diags)) + } + for i := range lastChildRoutes { + childRoute := routeModelMiddle{ + GroupBy: lastChildRoutes[i].GroupBy, + GroupInterval: lastChildRoutes[i].GroupInterval, + GroupWait: lastChildRoutes[i].GroupWait, + Match: lastChildRoutes[i].Match, + MatchRegex: lastChildRoutes[i].MatchRegex, + Matchers: lastChildRoutes[i].Matchers, + Receiver: lastChildRoutes[i].Receiver, + RepeatInterval: lastChildRoutes[i].RepeatInterval, + Routes: types.ListNull(getRouteListType()), + } + childRoutes = append(childRoutes, childRoute) + } + } + + childRoutesList := []observability.CreateAlertConfigRoutePayloadRoutesInner{} + for i := range childRoutes { + childRoute := childRoutes[i] + childRoutePayload, err := toMiddleRoutesToPayload(ctx, &childRoute) + if err != nil { + return nil, fmt.Errorf("mapping child route: %w", err) + } + childRoutesList = append(childRoutesList, *toChildRoutePayload(childRoutePayload)) + } + + childRoutesPayload = &childRoutesList + } + + return &observability.UpdateAlertConfigsPayloadRoute{ + GroupBy: groupByPayload, + GroupInterval: conversion.StringValueToPointer(routeTF.GroupInterval), + GroupWait: conversion.StringValueToPointer(routeTF.GroupWait), + Receiver: conversion.StringValueToPointer(routeTF.Receiver), + RepeatInterval: conversion.StringValueToPointer(routeTF.RepeatInterval), + Routes: childRoutesPayload, + }, nil +} + +func toMiddleRoutesToPayload(ctx context.Context, routeTF *routeModelMiddle) (*observability.UpdateAlertConfigsPayloadRoute, error) { if routeTF == nil { return nil, fmt.Errorf("nil route model") } @@ -2052,9 +2126,10 @@ func toRoutePayload(ctx context.Context, routeTF *routeModel) (*observability.Up var groupByPayload *[]string var matchPayload *map[string]interface{} var matchRegexPayload *map[string]interface{} + var matchersPayload *[]string var childRoutesPayload *[]observability.CreateAlertConfigRoutePayloadRoutesInner - if !routeTF.GroupBy.IsNull() && !routeTF.GroupBy.IsUnknown() { + if !utils.IsUndefined(routeTF.GroupBy) { groupByPayload = &[]string{} diags := routeTF.GroupBy.ElementsAs(ctx, groupByPayload, false) if diags.HasError() { @@ -2062,7 +2137,7 @@ func toRoutePayload(ctx context.Context, routeTF *routeModel) (*observability.Up } } - if !routeTF.Match.IsNull() && !routeTF.Match.IsUnknown() { + if !utils.IsUndefined(routeTF.Match) { matchMap, err := conversion.ToStringInterfaceMap(ctx, routeTF.Match) if err != nil { return nil, fmt.Errorf("mapping match: %w", err) @@ -2070,7 +2145,7 @@ func toRoutePayload(ctx context.Context, routeTF *routeModel) (*observability.Up matchPayload = &matchMap } - if !routeTF.MatchRegex.IsNull() && !routeTF.MatchRegex.IsUnknown() { + if !utils.IsUndefined(routeTF.MatchRegex) { matchRegexMap, err := conversion.ToStringInterfaceMap(ctx, routeTF.MatchRegex) if err != nil { return nil, fmt.Errorf("mapping match regex: %w", err) @@ -2078,8 +2153,16 @@ func toRoutePayload(ctx context.Context, routeTF *routeModel) (*observability.Up matchRegexPayload = &matchRegexMap } + if !utils.IsUndefined(routeTF.Matchers) { + matchersList, err := conversion.StringListToPointer(routeTF.Matchers) + if err != nil { + return nil, fmt.Errorf("mapping match regex: %w", err) + } + matchersPayload = matchersList + } + if !routeTF.Routes.IsNull() && !routeTF.Routes.IsUnknown() { - childRoutes := []routeModel{} + childRoutes := []routeModelMiddle{} diags := routeTF.Routes.ElementsAs(ctx, &childRoutes, false) if diags.HasError() { // If there is an error, we will try to map the child routes as if they are the last child routes @@ -2091,12 +2174,13 @@ func toRoutePayload(ctx context.Context, routeTF *routeModel) (*observability.Up return nil, fmt.Errorf("mapping child routes: %w", core.DiagsToError(diags)) } for i := range lastChildRoutes { - childRoute := routeModel{ + childRoute := routeModelMiddle{ GroupBy: lastChildRoutes[i].GroupBy, GroupInterval: lastChildRoutes[i].GroupInterval, GroupWait: lastChildRoutes[i].GroupWait, Match: lastChildRoutes[i].Match, MatchRegex: lastChildRoutes[i].MatchRegex, + Matchers: lastChildRoutes[i].Matchers, Receiver: lastChildRoutes[i].Receiver, RepeatInterval: lastChildRoutes[i].RepeatInterval, Routes: types.ListNull(getRouteListType()), @@ -2108,7 +2192,7 @@ func toRoutePayload(ctx context.Context, routeTF *routeModel) (*observability.Up childRoutesList := []observability.CreateAlertConfigRoutePayloadRoutesInner{} for i := range childRoutes { childRoute := childRoutes[i] - childRoutePayload, err := toRoutePayload(ctx, &childRoute) + childRoutePayload, err := toMiddleRoutesToPayload(ctx, &childRoute) if err != nil { return nil, fmt.Errorf("mapping child route: %w", err) } @@ -2124,6 +2208,7 @@ func toRoutePayload(ctx context.Context, routeTF *routeModel) (*observability.Up GroupWait: conversion.StringValueToPointer(routeTF.GroupWait), Match: matchPayload, MatchRe: matchRegexPayload, + Matchers: matchersPayload, Receiver: conversion.StringValueToPointer(routeTF.Receiver), RepeatInterval: conversion.StringValueToPointer(routeTF.RepeatInterval), Routes: childRoutesPayload, @@ -2140,6 +2225,7 @@ func toChildRoutePayload(in *observability.UpdateAlertConfigsPayloadRoute) *obse GroupWait: in.GroupWait, Match: in.Match, MatchRe: in.MatchRe, + Matchers: in.Matchers, Receiver: in.Receiver, RepeatInterval: in.RepeatInterval, // Routes not currently supported diff --git a/stackit/internal/services/observability/instance/resource_test.go b/stackit/internal/services/observability/instance/resource_test.go index 0c6a3aa5c..c4ec789c7 100644 --- a/stackit/internal/services/observability/instance/resource_test.go +++ b/stackit/internal/services/observability/instance/resource_test.go @@ -66,8 +66,6 @@ func fixtureRouteModel() basetypes.ObjectValue { }), "group_interval": types.StringValue("1m"), "group_wait": types.StringValue("1m"), - "match": types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), - "match_regex": types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), "receiver": types.StringValue("name"), "repeat_interval": types.StringValue("1m"), // "routes": types.ListNull(getRouteListType()), @@ -77,10 +75,14 @@ func fixtureRouteModel() basetypes.ObjectValue { types.StringValue("label1"), types.StringValue("label2"), }), - "group_interval": types.StringValue("1m"), - "group_wait": types.StringValue("1m"), - "match": types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), - "match_regex": types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), + "group_interval": types.StringValue("1m"), + "group_wait": types.StringValue("1m"), + "match": types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), + "match_regex": types.MapValueMust(types.StringType, map[string]attr.Value{"key": types.StringValue("value")}), + "matchers": types.ListValueMust(types.StringType, []attr.Value{ + types.StringValue("matcher1"), + types.StringValue("matcher2"), + }), "receiver": types.StringValue("name"), "repeat_interval": types.StringValue("1m"), }), @@ -93,8 +95,6 @@ func fixtureNullRouteModel() basetypes.ObjectValue { "group_by": types.ListNull(types.StringType), "group_interval": types.StringNull(), "group_wait": types.StringNull(), - "match": types.MapNull(types.StringType), - "match_regex": types.MapNull(types.StringType), "receiver": types.StringNull(), "repeat_interval": types.StringNull(), "routes": types.ListNull(getRouteListType()), @@ -167,8 +167,6 @@ func fixtureRoutePayload() *observability.UpdateAlertConfigsPayloadRoute { GroupBy: utils.Ptr([]string{"label1", "label2"}), GroupInterval: utils.Ptr("1m"), GroupWait: utils.Ptr("1m"), - Match: &map[string]interface{}{"key": "value"}, - MatchRe: &map[string]interface{}{"key": "value"}, Receiver: utils.Ptr("name"), RepeatInterval: utils.Ptr("1m"), Routes: &[]observability.CreateAlertConfigRoutePayloadRoutesInner{ @@ -178,6 +176,7 @@ func fixtureRoutePayload() *observability.UpdateAlertConfigsPayloadRoute { GroupWait: utils.Ptr("1m"), Match: &map[string]interface{}{"key": "value"}, MatchRe: &map[string]interface{}{"key": "value"}, + Matchers: &[]string{"matcher1", "matcher2"}, Receiver: utils.Ptr("name"), RepeatInterval: utils.Ptr("1m"), }, @@ -240,6 +239,7 @@ func fixtureRouteResponse() *observability.Route { GroupWait: utils.Ptr("1m"), Match: &map[string]string{"key": "value"}, MatchRe: &map[string]string{"key": "value"}, + Matchers: &[]string{"matcher1", "matcher2"}, Receiver: utils.Ptr("name"), RepeatInterval: utils.Ptr("1m"), Routes: &[]observability.RouteSerializer{ @@ -249,6 +249,7 @@ func fixtureRouteResponse() *observability.Route { GroupWait: utils.Ptr("1m"), Match: &map[string]string{"key": "value"}, MatchRe: &map[string]string{"key": "value"}, + Matchers: &[]string{"matcher1", "matcher2"}, Receiver: utils.Ptr("name"), RepeatInterval: utils.Ptr("1m"), }, @@ -294,13 +295,21 @@ func fixtureRouteAttributeSchema(route *schema.ListNestedAttribute, isDatasource }, }, "match": schema.MapAttribute{ - Description: routeDescriptions["match"], - Optional: !isDatasource, - Computed: isDatasource, - ElementType: types.StringType, + Description: routeDescriptions["match"], + DeprecationMessage: "Use `matchers` in the `routes` instead.", + Optional: !isDatasource, + Computed: isDatasource, + ElementType: types.StringType, }, "match_regex": schema.MapAttribute{ - Description: routeDescriptions["match_regex"], + Description: routeDescriptions["match_regex"], + DeprecationMessage: "Use `matchers` in the `routes` instead.", + Optional: !isDatasource, + Computed: isDatasource, + ElementType: types.StringType, + }, + "matchers": schema.ListAttribute{ + Description: routeDescriptions["matchers"], Optional: !isDatasource, Computed: isDatasource, ElementType: types.StringType, @@ -1478,6 +1487,7 @@ func TestGetRouteListTypeAux(t *testing.T) { "group_wait": types.StringType, "match": types.MapType{ElemType: types.StringType}, "match_regex": types.MapType{ElemType: types.StringType}, + "matchers": types.ListType{ElemType: types.StringType}, "receiver": types.StringType, "repeat_interval": types.StringType, }, @@ -1494,6 +1504,7 @@ func TestGetRouteListTypeAux(t *testing.T) { "group_wait": types.StringType, "match": types.MapType{ElemType: types.StringType}, "match_regex": types.MapType{ElemType: types.StringType}, + "matchers": types.ListType{ElemType: types.StringType}, "receiver": types.StringType, "repeat_interval": types.StringType, "routes": types.ListType{ElemType: types.ObjectType{AttrTypes: map[string]attr.Type{ @@ -1502,6 +1513,7 @@ func TestGetRouteListTypeAux(t *testing.T) { "group_wait": types.StringType, "match": types.MapType{ElemType: types.StringType}, "match_regex": types.MapType{ElemType: types.StringType}, + "matchers": types.ListType{ElemType: types.StringType}, "receiver": types.StringType, "repeat_interval": types.StringType, }}}, @@ -1519,6 +1531,7 @@ func TestGetRouteListTypeAux(t *testing.T) { "group_wait": types.StringType, "match": types.MapType{ElemType: types.StringType}, "match_regex": types.MapType{ElemType: types.StringType}, + "matchers": types.ListType{ElemType: types.StringType}, "receiver": types.StringType, "repeat_interval": types.StringType, },