Skip to content

Commit 61d37df

Browse files
authored
Bug Fix: Add API supported missing type to escalation policy (#2411)
* add missing step type notify_if_num_alerts_in_window * bump amixr-api-go-client version * update docs and remove field requirement. OnCall api does not enforce parameters
1 parent 72a7758 commit 61d37df

File tree

5 files changed

+143
-2
lines changed

5 files changed

+143
-2
lines changed

docs/resources/oncall_escalation.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ resource "grafana_oncall_escalation" "example_notify_step" {
6060

6161
- `escalation_chain_id` (String) The ID of the escalation chain.
6262
- `position` (Number) The position of the escalation step (starts from 0).
63-
- `type` (String) The type of escalation policy. Can be wait, notify_persons, notify_person_next_each_time, notify_on_call_from_schedule, trigger_webhook, notify_user_group, resolve, notify_whole_channel, notify_if_time_from_to, repeat_escalation, notify_team_members, declare_incident
63+
- `type` (String) The type of escalation policy. Can be wait, notify_persons, notify_person_next_each_time, notify_on_call_from_schedule, trigger_webhook, notify_user_group, resolve, notify_whole_channel, notify_if_time_from_to, notify_if_num_alerts_in_window, repeat_escalation, notify_team_members, declare_incident
6464

6565
### Optional
6666

@@ -72,6 +72,8 @@ resource "grafana_oncall_escalation" "example_notify_step" {
7272
- `notify_if_time_to` (String) The end of the time interval for notify_if_time_from_to type step in UTC (for example 18:00:00Z).
7373
- `notify_on_call_from_schedule` (String) ID of a Schedule for notify_on_call_from_schedule type step.
7474
- `notify_to_team_members` (String) The ID of a Team for a notify_team_members type step.
75+
- `num_alerts_in_window` (Number) Number of alerts that must occur within the time window to continue escalation for notify_if_num_alerts_in_window type step.
76+
- `num_minutes_in_window` (Number) Time window in minutes to count alerts for notify_if_num_alerts_in_window type step.
7577
- `persons_to_notify` (Set of String) The list of ID's of users for notify_persons type step.
7678
- `persons_to_notify_next_each_time` (Set of String) The list of ID's of users for notify_person_next_each_time type step.
7779
- `severity` (String) The severity of the incident for declare_incident type step.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ require (
88
github.com/fatih/color v1.18.0
99
github.com/go-openapi/runtime v0.28.0
1010
github.com/go-openapi/strfmt v0.23.0
11-
github.com/grafana/amixr-api-go-client v0.0.25
11+
github.com/grafana/amixr-api-go-client v0.0.27
1212
github.com/grafana/authlib/claims v0.0.0-20250120084028-e3328c576437
1313
github.com/grafana/fleet-management-api v1.0.0
1414
github.com/grafana/grafana-app-sdk v0.45.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
171171
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
172172
github.com/grafana/amixr-api-go-client v0.0.25 h1:tAQeJRuq9ihHotxq6/oEB6lIhuAdM+MUP0uPkNn1I3A=
173173
github.com/grafana/amixr-api-go-client v0.0.25/go.mod h1:ihgLhTVimmjASuZ06y/mQxPcYH3toAIuUVGK6flHsMU=
174+
github.com/grafana/amixr-api-go-client v0.0.27 h1:tmw7JcbX3D0N52mK60kYvAdRNFKH0aOb+9FKPaye2sc=
175+
github.com/grafana/amixr-api-go-client v0.0.27/go.mod h1:ihgLhTVimmjASuZ06y/mQxPcYH3toAIuUVGK6flHsMU=
174176
github.com/grafana/authlib/claims v0.0.0-20250120084028-e3328c576437 h1:OlwbIVFcYgMjnQhpbZwRPVNrvZKTodvPMqwb8yEqVW0=
175177
github.com/grafana/authlib/claims v0.0.0-20250120084028-e3328c576437/go.mod h1:r+F8H6awwjNQt/KPZ2GNwjk8TvsJ7/gxzkXN26GlL/A=
176178
github.com/grafana/fleet-management-api v1.0.0 h1:twb0kBgeNRcvQi7iDXq7AFhlM2mWcN76kTXleJuPA38=

internal/resources/oncall/resource_escalation.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ var escalationOptions = []string{
2323
"resolve",
2424
"notify_whole_channel",
2525
"notify_if_time_from_to",
26+
"notify_if_num_alerts_in_window",
2627
"repeat_escalation",
2728
"notify_team_members",
2829
"declare_incident",
@@ -79,6 +80,8 @@ func resourceEscalation() *common.Resource {
7980
"group_to_notify",
8081
"notify_if_time_from",
8182
"notify_if_time_to",
83+
"num_alerts_in_window",
84+
"num_minutes_in_window",
8285
},
8386
ValidateFunc: validation.IntBetween(60, 86400),
8487
Description: "The duration of delay for wait type step. (60-86400) seconds",
@@ -95,6 +98,8 @@ func resourceEscalation() *common.Resource {
9598
"group_to_notify",
9699
"notify_if_time_from",
97100
"notify_if_time_to",
101+
"num_alerts_in_window",
102+
"num_minutes_in_window",
98103
},
99104
Description: "ID of a Schedule for notify_on_call_from_schedule type step.",
100105
},
@@ -113,6 +118,8 @@ func resourceEscalation() *common.Resource {
113118
"group_to_notify",
114119
"notify_if_time_from",
115120
"notify_if_time_to",
121+
"num_alerts_in_window",
122+
"num_minutes_in_window",
116123
},
117124
Description: "The list of ID's of users for notify_persons type step.",
118125
},
@@ -209,6 +216,34 @@ func resourceEscalation() *common.Resource {
209216
},
210217
Description: "The end of the time interval for notify_if_time_from_to type step in UTC (for example 18:00:00Z).",
211218
},
219+
"num_alerts_in_window": {
220+
Type: schema.TypeInt,
221+
Optional: true,
222+
ConflictsWith: []string{
223+
"duration",
224+
"notify_on_call_from_schedule",
225+
"persons_to_notify",
226+
"persons_to_notify_next_each_time",
227+
"notify_to_team_members",
228+
"action_to_trigger",
229+
"group_to_notify",
230+
},
231+
Description: "Number of alerts that must occur within the time window to continue escalation for notify_if_num_alerts_in_window type step.",
232+
},
233+
"num_minutes_in_window": {
234+
Type: schema.TypeInt,
235+
Optional: true,
236+
ConflictsWith: []string{
237+
"duration",
238+
"notify_on_call_from_schedule",
239+
"persons_to_notify",
240+
"persons_to_notify_next_each_time",
241+
"notify_to_team_members",
242+
"action_to_trigger",
243+
"group_to_notify",
244+
},
245+
Description: "Time window in minutes to count alerts for notify_if_num_alerts_in_window type step.",
246+
},
212247
"severity": {
213248
Type: schema.TypeString,
214249
Optional: true,
@@ -353,6 +388,24 @@ func resourceEscalationCreate(ctx context.Context, d *schema.ResourceData, clien
353388
}
354389
}
355390

391+
numAlertsInWindowData, numAlertsInWindowDataOk := d.GetOk("num_alerts_in_window")
392+
if numAlertsInWindowDataOk {
393+
if typeData == "notify_if_num_alerts_in_window" {
394+
createOptions.NumAlertsInWindow = numAlertsInWindowData.(int)
395+
} else {
396+
return diag.Errorf("num_alerts_in_window can not be set with type: %s", typeData)
397+
}
398+
}
399+
400+
numMinutesInWindowData, numMinutesInWindowDataOk := d.GetOk("num_minutes_in_window")
401+
if numMinutesInWindowDataOk {
402+
if typeData == "notify_if_num_alerts_in_window" {
403+
createOptions.NumMinutesInWindow = numMinutesInWindowData.(int)
404+
} else {
405+
return diag.Errorf("num_minutes_in_window can not be set with type: %s", typeData)
406+
}
407+
}
408+
356409
importanceData := d.Get("important").(bool)
357410
createOptions.Important = &importanceData
358411

@@ -414,6 +467,12 @@ func resourceEscalationRead(ctx context.Context, d *schema.ResourceData, client
414467
if escalation.NotifyIfTimeTo != nil {
415468
d.Set("notify_if_time_to", escalation.NotifyIfTimeTo)
416469
}
470+
if escalation.NumAlertsInWindow != nil {
471+
d.Set("num_alerts_in_window", escalation.NumAlertsInWindow)
472+
}
473+
if escalation.NumMinutesInWindow != nil {
474+
d.Set("num_minutes_in_window", escalation.NumMinutesInWindow)
475+
}
417476

418477
return nil
419478
}
@@ -501,6 +560,20 @@ func resourceEscalationUpdate(ctx context.Context, d *schema.ResourceData, clien
501560
}
502561
}
503562

563+
numAlertsInWindowData, numAlertsInWindowDataOk := d.GetOk("num_alerts_in_window")
564+
if numAlertsInWindowDataOk {
565+
if typeData == "notify_if_num_alerts_in_window" {
566+
updateOptions.NumAlertsInWindow = numAlertsInWindowData.(int)
567+
}
568+
}
569+
570+
numMinutesInWindowData, numMinutesInWindowDataOk := d.GetOk("num_minutes_in_window")
571+
if numMinutesInWindowDataOk {
572+
if typeData == "notify_if_num_alerts_in_window" {
573+
updateOptions.NumMinutesInWindow = numMinutesInWindowData.(int)
574+
}
575+
}
576+
504577
positionData := d.Get("position").(int)
505578
updateOptions.Position = &positionData
506579

internal/resources/oncall/resource_escalation_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package oncall_test
22

33
import (
44
"fmt"
5+
"regexp"
56
"testing"
67

78
onCallAPI "github.com/grafana/amixr-api-go-client"
@@ -43,6 +44,12 @@ func TestAccOnCallEscalation_basic(t *testing.T) {
4344
resource.TestCheckResourceAttr("grafana_oncall_escalation.test-acc-escalation-policy-declare-incident", "type", "declare_incident"),
4445
resource.TestCheckResourceAttr("grafana_oncall_escalation.test-acc-escalation-policy-declare-incident", "position", "3"),
4546
resource.TestCheckResourceAttrSet("grafana_oncall_escalation.test-acc-escalation-policy-declare-incident", "severity"),
47+
48+
testAccCheckOnCallEscalationResourceExists("grafana_oncall_escalation.test-acc-escalation-policy-num-alerts"),
49+
resource.TestCheckResourceAttr("grafana_oncall_escalation.test-acc-escalation-policy-num-alerts", "type", "notify_if_num_alerts_in_window"),
50+
resource.TestCheckResourceAttr("grafana_oncall_escalation.test-acc-escalation-policy-num-alerts", "position", "4"),
51+
resource.TestCheckResourceAttr("grafana_oncall_escalation.test-acc-escalation-policy-num-alerts", "num_alerts_in_window", "3"),
52+
resource.TestCheckResourceAttr("grafana_oncall_escalation.test-acc-escalation-policy-num-alerts", "num_minutes_in_window", "5"),
4653
),
4754
},
4855
{
@@ -60,6 +67,11 @@ func TestAccOnCallEscalation_basic(t *testing.T) {
6067
ResourceName: "grafana_oncall_escalation.test-acc-escalation-policy-team",
6168
ImportStateVerify: true,
6269
},
70+
{
71+
ImportState: true,
72+
ResourceName: "grafana_oncall_escalation.test-acc-escalation-policy-num-alerts",
73+
ImportStateVerify: true,
74+
},
6375
},
6476
})
6577
}
@@ -125,9 +137,61 @@ resource "grafana_oncall_escalation" "test-acc-escalation-policy-declare-inciden
125137
severity = "critical"
126138
position = 3
127139
}
140+
141+
resource "grafana_oncall_escalation" "test-acc-escalation-policy-num-alerts" {
142+
escalation_chain_id = grafana_oncall_escalation_chain.test-acc-escalation-chain.id
143+
type = "notify_if_num_alerts_in_window"
144+
num_alerts_in_window = 3
145+
num_minutes_in_window = 5
146+
position = 4
147+
}
128148
`, riName, riName, riName, reType, reDuration)
129149
}
130150

151+
func TestAccOnCallEscalation_notifyIfNumAlertsInWindow_wrongType(t *testing.T) {
152+
testutils.CheckCloudInstanceTestsEnabled(t)
153+
154+
riName := fmt.Sprintf("test-acc-%s", acctest.RandString(8))
155+
156+
resource.ParallelTest(t, resource.TestCase{
157+
ProtoV5ProviderFactories: testutils.ProtoV5ProviderFactories,
158+
Steps: []resource.TestStep{
159+
{
160+
Config: testAccOnCallEscalationNotifyIfNumAlertsInWindowConfigWrongType(riName),
161+
ExpectError: regexp.MustCompile(`.*num_alerts_in_window.*conflicts with.*`),
162+
},
163+
},
164+
})
165+
}
166+
167+
func testAccOnCallEscalationNotifyIfNumAlertsInWindowConfigWrongType(riName string) string {
168+
return fmt.Sprintf(`
169+
resource "grafana_oncall_integration" "test-acc-integration" {
170+
name = "%s"
171+
type = "grafana"
172+
default_route {
173+
}
174+
}
175+
176+
resource "grafana_oncall_escalation_chain" "test-acc-escalation-chain" {
177+
name = "acc-test-%s"
178+
}
179+
180+
resource "grafana_oncall_escalation" "test-acc-escalation-wrong-type" {
181+
escalation_chain_id = grafana_oncall_escalation_chain.test-acc-escalation-chain.id
182+
type = "notify_team_members"
183+
notify_to_team_members = grafana_team.test-acc-team.id
184+
num_alerts_in_window = 3
185+
num_minutes_in_window = 5
186+
position = 0
187+
}
188+
189+
resource "grafana_team" "test-acc-team" {
190+
name = "acc-escalation-test-%s"
191+
}
192+
`, riName, riName, riName)
193+
}
194+
131195
func testAccCheckOnCallEscalationResourceExists(name string) resource.TestCheckFunc {
132196
return func(s *terraform.State) error {
133197
rs, ok := s.RootModule().Resources[name]

0 commit comments

Comments
 (0)