Skip to content

Commit 3892009

Browse files
modular-magicianJames Edouard
and
James Edouard
authored
Add support for forecast options in AlertPolicy (#7926) (#14616)
Signed-off-by: Modular Magician <[email protected]> Co-authored-by: James Edouard <[email protected]>
1 parent 6b01786 commit 3892009

File tree

4 files changed

+184
-5
lines changed

4 files changed

+184
-5
lines changed

.changelog/7926.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
monitoring: added `forecast_options` field to `google_monitoring_alert_policy` resource
3+
```

google/resource_monitoring_alert_policy.go

+74
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,31 @@ resource labels, and metric labels. This
672672
field may not exceed 2048 Unicode characters
673673
in length.`,
674674
},
675+
"forecast_options": {
676+
Type: schema.TypeList,
677+
Optional: true,
678+
Description: `When this field is present, the 'MetricThreshold'
679+
condition forecasts whether the time series is
680+
predicted to violate the threshold within the
681+
'forecastHorizon'. When this field is not set, the
682+
'MetricThreshold' tests the current value of the
683+
timeseries against the threshold.`,
684+
MaxItems: 1,
685+
Elem: &schema.Resource{
686+
Schema: map[string]*schema.Schema{
687+
"forecast_horizon": {
688+
Type: schema.TypeString,
689+
Required: true,
690+
Description: `The length of time into the future to forecast
691+
whether a timeseries will violate the threshold.
692+
If the predicted value is found to violate the
693+
threshold, and the violation is observed in all
694+
forecasts made for the Configured 'duration',
695+
then the timeseries is considered to be failing.`,
696+
},
697+
},
698+
},
699+
},
675700
"threshold_value": {
676701
Type: schema.TypeFloat,
677702
Optional: true,
@@ -1518,6 +1543,8 @@ func flattenMonitoringAlertPolicyConditionsConditionThreshold(v interface{}, d *
15181543
flattenMonitoringAlertPolicyConditionsConditionThresholdDenominatorAggregations(original["denominatorAggregations"], d, config)
15191544
transformed["duration"] =
15201545
flattenMonitoringAlertPolicyConditionsConditionThresholdDuration(original["duration"], d, config)
1546+
transformed["forecast_options"] =
1547+
flattenMonitoringAlertPolicyConditionsConditionThresholdForecastOptions(original["forecastOptions"], d, config)
15211548
transformed["comparison"] =
15221549
flattenMonitoringAlertPolicyConditionsConditionThresholdComparison(original["comparison"], d, config)
15231550
transformed["trigger"] =
@@ -1579,6 +1606,23 @@ func flattenMonitoringAlertPolicyConditionsConditionThresholdDuration(v interfac
15791606
return v
15801607
}
15811608

1609+
func flattenMonitoringAlertPolicyConditionsConditionThresholdForecastOptions(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
1610+
if v == nil {
1611+
return nil
1612+
}
1613+
original := v.(map[string]interface{})
1614+
if len(original) == 0 {
1615+
return nil
1616+
}
1617+
transformed := make(map[string]interface{})
1618+
transformed["forecast_horizon"] =
1619+
flattenMonitoringAlertPolicyConditionsConditionThresholdForecastOptionsForecastHorizon(original["forecastHorizon"], d, config)
1620+
return []interface{}{transformed}
1621+
}
1622+
func flattenMonitoringAlertPolicyConditionsConditionThresholdForecastOptionsForecastHorizon(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
1623+
return v
1624+
}
1625+
15821626
func flattenMonitoringAlertPolicyConditionsConditionThresholdComparison(v interface{}, d *schema.ResourceData, config *transport_tpg.Config) interface{} {
15831627
return v
15841628
}
@@ -2124,6 +2168,13 @@ func expandMonitoringAlertPolicyConditionsConditionThreshold(v interface{}, d tp
21242168
transformed["duration"] = transformedDuration
21252169
}
21262170

2171+
transformedForecastOptions, err := expandMonitoringAlertPolicyConditionsConditionThresholdForecastOptions(original["forecast_options"], d, config)
2172+
if err != nil {
2173+
return nil, err
2174+
} else if val := reflect.ValueOf(transformedForecastOptions); val.IsValid() && !tpgresource.IsEmptyValue(val) {
2175+
transformed["forecastOptions"] = transformedForecastOptions
2176+
}
2177+
21272178
transformedComparison, err := expandMonitoringAlertPolicyConditionsConditionThresholdComparison(original["comparison"], d, config)
21282179
if err != nil {
21292180
return nil, err
@@ -2233,6 +2284,29 @@ func expandMonitoringAlertPolicyConditionsConditionThresholdDuration(v interface
22332284
return v, nil
22342285
}
22352286

2287+
func expandMonitoringAlertPolicyConditionsConditionThresholdForecastOptions(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
2288+
l := v.([]interface{})
2289+
if len(l) == 0 || l[0] == nil {
2290+
return nil, nil
2291+
}
2292+
raw := l[0]
2293+
original := raw.(map[string]interface{})
2294+
transformed := make(map[string]interface{})
2295+
2296+
transformedForecastHorizon, err := expandMonitoringAlertPolicyConditionsConditionThresholdForecastOptionsForecastHorizon(original["forecast_horizon"], d, config)
2297+
if err != nil {
2298+
return nil, err
2299+
} else if val := reflect.ValueOf(transformedForecastHorizon); val.IsValid() && !tpgresource.IsEmptyValue(val) {
2300+
transformed["forecastHorizon"] = transformedForecastHorizon
2301+
}
2302+
2303+
return transformed, nil
2304+
}
2305+
2306+
func expandMonitoringAlertPolicyConditionsConditionThresholdForecastOptionsForecastHorizon(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
2307+
return v, nil
2308+
}
2309+
22362310
func expandMonitoringAlertPolicyConditionsConditionThresholdComparison(v interface{}, d tpgresource.TerraformResourceData, config *transport_tpg.Config) (interface{}, error) {
22372311
return v, nil
22382312
}

google/resource_monitoring_alert_policy_test.go

+58-5
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ import (
1515

1616
func TestAccMonitoringAlertPolicy(t *testing.T) {
1717
testCases := map[string]func(t *testing.T){
18-
"basic": testAccMonitoringAlertPolicy_basic,
19-
"full": testAccMonitoringAlertPolicy_full,
20-
"update": testAccMonitoringAlertPolicy_update,
21-
"mql": testAccMonitoringAlertPolicy_mql,
22-
"log": testAccMonitoringAlertPolicy_log,
18+
"basic": testAccMonitoringAlertPolicy_basic,
19+
"full": testAccMonitoringAlertPolicy_full,
20+
"update": testAccMonitoringAlertPolicy_update,
21+
"mql": testAccMonitoringAlertPolicy_mql,
22+
"log": testAccMonitoringAlertPolicy_log,
23+
"forecast": testAccMonitoringAlertPolicy_forecast,
2324
}
2425

2526
for name, tc := range testCases {
@@ -181,6 +182,29 @@ func testAccCheckAlertPolicyDestroyProducer(t *testing.T) func(s *terraform.Stat
181182
}
182183
}
183184

185+
func testAccMonitoringAlertPolicy_forecast(t *testing.T) {
186+
187+
alertName := fmt.Sprintf("tf-test-%s", RandString(t, 10))
188+
conditionName := fmt.Sprintf("tf-test-%s", RandString(t, 10))
189+
filter := `metric.type=\"compute.googleapis.com/instance/disk/write_bytes_count\" AND resource.type=\"gce_instance\"`
190+
191+
VcrTest(t, resource.TestCase{
192+
PreCheck: func() { acctest.AccTestPreCheck(t) },
193+
ProtoV5ProviderFactories: ProtoV5ProviderFactories(t),
194+
CheckDestroy: testAccCheckAlertPolicyDestroyProducer(t),
195+
Steps: []resource.TestStep{
196+
{
197+
Config: testAccMonitoringAlertPolicy_forecastCfg(alertName, conditionName, "ALIGN_RATE", filter),
198+
},
199+
{
200+
ResourceName: "google_monitoring_alert_policy.forecast",
201+
ImportState: true,
202+
ImportStateVerify: true,
203+
},
204+
},
205+
})
206+
}
207+
184208
func testAccMonitoringAlertPolicy_basicCfg(alertName, conditionName, aligner, filter string) string {
185209
return fmt.Sprintf(`
186210
resource "google_monitoring_alert_policy" "basic" {
@@ -335,3 +359,32 @@ resource "google_monitoring_alert_policy" "log" {
335359
}
336360
`, alertName, conditionName)
337361
}
362+
363+
func testAccMonitoringAlertPolicy_forecastCfg(alertName, conditionName, aligner, filter string) string {
364+
return fmt.Sprintf(`
365+
resource "google_monitoring_alert_policy" "forecast" {
366+
display_name = "%s"
367+
enabled = true
368+
combiner = "OR"
369+
370+
conditions {
371+
display_name = "%s"
372+
373+
condition_threshold {
374+
aggregations {
375+
alignment_period = "60s"
376+
per_series_aligner = "%s"
377+
}
378+
379+
duration = "60s"
380+
forecast_options {
381+
forecast_horizon = "3600s"
382+
}
383+
comparison = "COMPARISON_GT"
384+
filter = "%s"
385+
threshold_value = "0.5"
386+
}
387+
}
388+
}
389+
`, alertName, conditionName, aligner, filter)
390+
}

website/docs/r/monitoring_alert_policy.html.markdown

+49
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,34 @@ resource "google_monitoring_alert_policy" "alert_policy" {
8383
}
8484
}
8585
```
86+
## Example Usage - Monitoring Alert Policy Forecast Options
87+
88+
89+
```hcl
90+
resource "google_monitoring_alert_policy" "alert_policy" {
91+
display_name = "My Alert Policy"
92+
combiner = "OR"
93+
conditions {
94+
display_name = "test condition"
95+
condition_threshold {
96+
filter = "metric.type=\"compute.googleapis.com/instance/disk/write_bytes_count\" AND resource.type=\"gce_instance\""
97+
duration = "60s"
98+
forecast_options {
99+
forecast_horizon = "3600s"
100+
}
101+
comparison = "COMPARISON_GT"
102+
aggregations {
103+
alignment_period = "60s"
104+
per_series_aligner = "ALIGN_RATE"
105+
}
106+
}
107+
}
108+
109+
user_labels = {
110+
foo = "bar"
111+
}
112+
}
113+
```
86114

87115
## Argument Reference
88116

@@ -427,6 +455,16 @@ The following arguments are supported:
427455
that unhealthy states are detected and
428456
alerted on quickly.
429457

458+
* `forecast_options` -
459+
(Optional)
460+
When this field is present, the `MetricThreshold`
461+
condition forecasts whether the time series is
462+
predicted to violate the threshold within the
463+
`forecastHorizon`. When this field is not set, the
464+
`MetricThreshold` tests the current value of the
465+
timeseries against the threshold.
466+
Structure is [documented below](#nested_forecast_options).
467+
430468
* `comparison` -
431469
(Required)
432470
The comparison to apply between the time
@@ -580,6 +618,17 @@ The following arguments are supported:
580618
returned.
581619
Possible values are: `REDUCE_NONE`, `REDUCE_MEAN`, `REDUCE_MIN`, `REDUCE_MAX`, `REDUCE_SUM`, `REDUCE_STDDEV`, `REDUCE_COUNT`, `REDUCE_COUNT_TRUE`, `REDUCE_COUNT_FALSE`, `REDUCE_FRACTION_TRUE`, `REDUCE_PERCENTILE_99`, `REDUCE_PERCENTILE_95`, `REDUCE_PERCENTILE_50`, `REDUCE_PERCENTILE_05`.
582620

621+
<a name="nested_forecast_options"></a>The `forecast_options` block supports:
622+
623+
* `forecast_horizon` -
624+
(Required)
625+
The length of time into the future to forecast
626+
whether a timeseries will violate the threshold.
627+
If the predicted value is found to violate the
628+
threshold, and the violation is observed in all
629+
forecasts made for the Configured `duration`,
630+
then the timeseries is considered to be failing.
631+
583632
<a name="nested_trigger"></a>The `trigger` block supports:
584633

585634
* `percent` -

0 commit comments

Comments
 (0)