Skip to content

Commit d6718e8

Browse files
Merge pull request #463 from SumoLogic/SUMO-207606-monitor-trigger-condition-empty-validations
SUMO-207606, added validations to fail on terraform plan if empty tri…
2 parents db62882 + ca211f9 commit d6718e8

File tree

2 files changed

+148
-26
lines changed

2 files changed

+148
-26
lines changed

sumologic/resource_sumologic_monitors_library_monitor.go

Lines changed: 82 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ func resourceSumologicMonitorsLibraryMonitor() *schema.Resource {
146146
Elem: &schema.Resource{
147147
Schema: logsStaticTriggerConditionSchema,
148148
},
149+
AtLeastOneOf: triggerConditionsAtleatOneKey,
149150
},
150151
"metrics_static_condition": {
151152
Type: schema.TypeList,
@@ -371,13 +372,50 @@ func resourceSumologicMonitorsLibraryMonitor() *schema.Resource {
371372
}
372373
}
373374

375+
var (
376+
triggerConditionsAtleatOneKey = []string{
377+
"trigger_conditions.0.logs_static_condition",
378+
"trigger_conditions.0.metrics_static_condition",
379+
"trigger_conditions.0.logs_outlier_condition",
380+
"trigger_conditions.0.metrics_outlier_condition",
381+
"trigger_conditions.0.logs_missing_data_condition",
382+
"trigger_conditions.0.metrics_missing_data_condition",
383+
"trigger_conditions.0.slo_sli_condition",
384+
"trigger_conditions.0.slo_burn_rate_condition",
385+
}
386+
logStaticConditionCriticalOrWarningAtleastOneKeys = []string{
387+
"trigger_conditions.0.logs_static_condition.0.warning",
388+
"trigger_conditions.0.logs_static_condition.0.critical",
389+
}
390+
metricsStaticConditionCriticalOrWarningAtleastOneKeys = []string{
391+
"trigger_conditions.0.metrics_static_condition.0.warning",
392+
"trigger_conditions.0.metrics_static_condition.0.critical",
393+
}
394+
logsOutlierConditionCriticalOrWarningAtleastOneKeys = []string{
395+
"trigger_conditions.0.logs_outlier_condition.0.warning",
396+
"trigger_conditions.0.logs_outlier_condition.0.critical",
397+
}
398+
metricsOutlierConditionCriticalOrWarningAtleastOneKeys = []string{
399+
"trigger_conditions.0.metrics_outlier_condition.0.warning",
400+
"trigger_conditions.0.metrics_outlier_condition.0.critical",
401+
}
402+
sloSLIConditionCriticalOrWarningAtleastOneKeys = []string{
403+
"trigger_conditions.0.slo_sli_condition.0.warning",
404+
"trigger_conditions.0.slo_sli_condition.0.critical",
405+
}
406+
sloBurnRateConditionCriticalOrWarningAtleastOneKeys = []string{
407+
"trigger_conditions.0.slo_burn_rate_condition.0.warning",
408+
"trigger_conditions.0.slo_burn_rate_condition.0.critical",
409+
}
410+
)
411+
374412
// Trigger Condition schemas
375413
var logsStaticTriggerConditionSchema = map[string]*schema.Schema{
376414
"field": {
377415
Type: schema.TypeString,
378416
Optional: true,
379417
},
380-
"critical": nested(true, schemaMap{
418+
"critical": nestedWithAtleastOneOfKeys(true, schemaMap{
381419
"time_range": &timeRangeSchema,
382420
"alert": nested(false, schemaMap{
383421
"threshold": &thresholdSchema,
@@ -388,8 +426,8 @@ var logsStaticTriggerConditionSchema = map[string]*schema.Schema{
388426
"threshold_type": &thresholdTypeSchema,
389427
"resolution_window": &resolutionWindowSchema,
390428
}),
391-
}),
392-
"warning": nested(true, schemaMap{
429+
}, logStaticConditionCriticalOrWarningAtleastOneKeys),
430+
"warning": nestedWithAtleastOneOfKeys(true, schemaMap{
393431
"time_range": &timeRangeSchema,
394432
"alert": nested(false, schemaMap{
395433
"threshold": &thresholdSchema,
@@ -400,11 +438,11 @@ var logsStaticTriggerConditionSchema = map[string]*schema.Schema{
400438
"threshold_type": &thresholdTypeSchema,
401439
"resolution_window": &resolutionWindowSchema,
402440
}),
403-
}),
441+
}, logStaticConditionCriticalOrWarningAtleastOneKeys),
404442
}
405443

406444
var metricsStaticTriggerConditionSchema = map[string]*schema.Schema{
407-
"critical": nested(true, schemaMap{
445+
"critical": nestedWithAtleastOneOfKeys(true, schemaMap{
408446
"time_range": &timeRangeSchema,
409447
"occurrence_type": &occurrenceTypeSchema,
410448
"alert": nested(false, schemaMap{
@@ -416,8 +454,8 @@ var metricsStaticTriggerConditionSchema = map[string]*schema.Schema{
416454
"threshold_type": &thresholdTypeSchema,
417455
"occurrence_type": &occurrenceTypeOptSchema,
418456
}),
419-
}),
420-
"warning": nested(true, schemaMap{
457+
}, metricsStaticConditionCriticalOrWarningAtleastOneKeys),
458+
"warning": nestedWithAtleastOneOfKeys(true, schemaMap{
421459
"time_range": &timeRangeSchema,
422460
"occurrence_type": &occurrenceTypeSchema,
423461
"alert": nested(false, schemaMap{
@@ -429,7 +467,7 @@ var metricsStaticTriggerConditionSchema = map[string]*schema.Schema{
429467
"threshold_type": &thresholdTypeSchema,
430468
"occurrence_type": &occurrenceTypeOptSchema,
431469
}),
432-
}),
470+
}, metricsStaticConditionCriticalOrWarningAtleastOneKeys),
433471
}
434472

435473
var logsOutlierTriggerConditionSchema = map[string]*schema.Schema{
@@ -442,16 +480,16 @@ var logsOutlierTriggerConditionSchema = map[string]*schema.Schema{
442480
Optional: true,
443481
ValidateFunc: validation.StringInSlice([]string{"Both", "Up", "Down"}, false),
444482
},
445-
"critical": nested(true, schemaMap{
483+
"critical": nestedWithAtleastOneOfKeys(true, schemaMap{
446484
"window": &windowSchema,
447485
"consecutive": &consecutiveSchema,
448486
"threshold": &thresholdSchema,
449-
}),
450-
"warning": nested(true, schemaMap{
487+
}, logsOutlierConditionCriticalOrWarningAtleastOneKeys),
488+
"warning": nestedWithAtleastOneOfKeys(true, schemaMap{
451489
"window": &windowSchema,
452490
"consecutive": &consecutiveSchema,
453491
"threshold": &thresholdSchema,
454-
}),
492+
}, logsOutlierConditionCriticalOrWarningAtleastOneKeys),
455493
}
456494

457495
var metricsOutlierTriggerConditionSchema = map[string]*schema.Schema{
@@ -460,14 +498,14 @@ var metricsOutlierTriggerConditionSchema = map[string]*schema.Schema{
460498
Optional: true,
461499
ValidateFunc: validation.StringInSlice([]string{"Both", "Up", "Down"}, false),
462500
},
463-
"critical": nested(true, schemaMap{
501+
"critical": nestedWithAtleastOneOfKeys(true, schemaMap{
464502
"baseline_window": &baselineWindowSchema,
465503
"threshold": &thresholdSchema,
466-
}),
467-
"warning": nested(true, schemaMap{
504+
}, metricsOutlierConditionCriticalOrWarningAtleastOneKeys),
505+
"warning": nestedWithAtleastOneOfKeys(true, schemaMap{
468506
"baseline_window": &baselineWindowSchema,
469507
"threshold": &thresholdSchema,
470-
}),
508+
}, metricsOutlierConditionCriticalOrWarningAtleastOneKeys),
471509
}
472510

473511
var logsMissingDataTriggerConditionSchema = map[string]*schema.Schema{
@@ -484,39 +522,39 @@ var metricsMissingDataTriggerConditionSchema = map[string]*schema.Schema{
484522
}
485523

486524
var sloSLITriggerConditionSchema = map[string]*schema.Schema{
487-
"critical": nested(true, schemaMap{
525+
"critical": nestedWithAtleastOneOfKeys(true, schemaMap{
488526
"sli_threshold": {
489527
Type: schema.TypeFloat,
490528
Required: true,
491529
ValidateFunc: validation.FloatBetween(0, 100),
492530
},
493-
}),
494-
"warning": nested(true, schemaMap{
531+
}, sloSLIConditionCriticalOrWarningAtleastOneKeys),
532+
"warning": nestedWithAtleastOneOfKeys(true, schemaMap{
495533
"sli_threshold": {
496534
Type: schema.TypeFloat,
497535
Required: true,
498536
ValidateFunc: validation.FloatBetween(0, 100),
499537
},
500-
}),
538+
}, sloSLIConditionCriticalOrWarningAtleastOneKeys),
501539
}
502540

503541
var sloBurnRateTriggerConditionSchema = map[string]*schema.Schema{
504-
"critical": nested(true, schemaMap{
542+
"critical": nestedWithAtleastOneOfKeys(true, schemaMap{
505543
"time_range": &timeRangeSchema,
506544
"burn_rate_threshold": {
507545
Type: schema.TypeFloat,
508546
Required: true,
509547
ValidateFunc: validation.FloatAtLeast(0),
510548
},
511-
}),
512-
"warning": nested(true, schemaMap{
549+
}, sloBurnRateConditionCriticalOrWarningAtleastOneKeys),
550+
"warning": nestedWithAtleastOneOfKeys(true, schemaMap{
513551
"time_range": &timeRangeSchema,
514552
"burn_rate_threshold": {
515553
Type: schema.TypeFloat,
516554
Required: true,
517555
ValidateFunc: validation.FloatAtLeast(0),
518556
},
519-
}),
557+
}, sloBurnRateConditionCriticalOrWarningAtleastOneKeys),
520558
}
521559

522560
var occurrenceTypeSchema = schema.Schema{
@@ -1430,6 +1468,26 @@ func nested(optional bool, sch map[string]*schema.Schema) *schema.Schema {
14301468
}
14311469
}
14321470

1471+
func nestedWithAtleastOneOfKeys(optional bool, sch map[string]*schema.Schema, atleastOneOfKeys []string) *schema.Schema {
1472+
if optional {
1473+
return &schema.Schema{
1474+
Type: schema.TypeList,
1475+
Optional: true,
1476+
MaxItems: 1,
1477+
Elem: toResource(sch),
1478+
AtLeastOneOf: atleastOneOfKeys,
1479+
}
1480+
} else {
1481+
return &schema.Schema{
1482+
Type: schema.TypeList,
1483+
Required: true,
1484+
MaxItems: 1,
1485+
Elem: toResource(sch),
1486+
AtLeastOneOf: atleastOneOfKeys,
1487+
}
1488+
}
1489+
}
1490+
14331491
func (condition *TriggerCondition) readFrom(block map[string]interface{}) {
14341492
for k, v := range block {
14351493
switch k {

sumologic/resource_sumologic_monitors_library_monitor_test.go

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func TestSumologicMonitorsLibraryMonitor_conversionsToFromTriggerConditionsShoul
8181
}
8282
}
8383

84-
func TestAccSumologicMonitorsLibraryMonitor_schemaValidations(t *testing.T) {
84+
func TestAccSumologicMonitorsLibraryMonitor_schemaTriggerValidations(t *testing.T) {
8585
var monitorsLibraryMonitor MonitorsLibraryMonitor
8686
config := `
8787
resource "sumologic_monitor" "test" {
@@ -106,6 +106,27 @@ func TestAccSumologicMonitorsLibraryMonitor_schemaValidations(t *testing.T) {
106106
})
107107
}
108108

109+
func TestAccSumologicMonitorsLibraryMonitor_schemaTriggerConditionValidations(t *testing.T) {
110+
var monitorsLibraryMonitor MonitorsLibraryMonitor
111+
for _, monitorConfig := range allInvalidTriggerConditionMonitorResources {
112+
testNameSuffix := acctest.RandString(16)
113+
114+
testName := "terraform_test_invalid_monitor_" + testNameSuffix
115+
116+
resource.Test(t, resource.TestCase{
117+
PreCheck: func() { testAccPreCheck(t) },
118+
Providers: testAccProviders,
119+
CheckDestroy: testAccCheckMonitorsLibraryMonitorDestroy(monitorsLibraryMonitor),
120+
Steps: []resource.TestStep{
121+
{
122+
Config: monitorConfig(testName),
123+
ExpectError: regexp.MustCompile("config is invalid"),
124+
},
125+
},
126+
})
127+
}
128+
}
129+
109130
func TestAccSumologicMonitorsLibraryMonitor_triggersTimeRangeDiffSuppression(t *testing.T) {
110131
var monitorsLibraryMonitor MonitorsLibraryMonitor
111132
canonicalTimeRange := "1h"
@@ -1026,7 +1047,7 @@ func exampleMonitorWithTriggerCondition(
10261047
trigger string,
10271048
triggerTys []string) string {
10281049
triggerTysStr := `"` + strings.Join(triggerTys, `","`) + `"`
1029-
return fmt.Sprintf(`
1050+
var resourceText = fmt.Sprintf(`
10301051
resource "sumologic_monitor" "test" {
10311052
name = "%s"
10321053
description = "terraform_test_monitor_description"
@@ -1053,6 +1074,7 @@ resource "sumologic_monitor" "test" {
10531074
}
10541075
playbook = "This is a test playbook"
10551076
}`, testName, monitorType, query, trigger, triggerTysStr)
1077+
return resourceText
10561078
}
10571079

10581080
var exampleLogsStaticTriggerConditionBlock = `
@@ -1210,6 +1232,26 @@ var allExampleMonitors = []func(testName string) string{
12101232
exampleMetricsMissingDataMonitor,
12111233
}
12121234

1235+
func testAccSumologicMonitorsLibraryMonitorWithInvalidTriggerCondition(testName string, triggerCondition string) string {
1236+
return fmt.Sprintf(`
1237+
resource "sumologic_monitor" "test" {
1238+
name = "terraform_test_monitor_%s"
1239+
description = "terraform_test_monitor_description"
1240+
type = "MonitorsLibraryMonitor"
1241+
is_disabled = false
1242+
content_type = "Monitor"
1243+
monitor_type = "Logs"
1244+
evaluation_delay = "60m"
1245+
queries {
1246+
row_id = "A"
1247+
query = "_sourceCategory=monitor-manager error"
1248+
}
1249+
trigger_conditions {
1250+
%s
1251+
}
1252+
}`, testName, triggerCondition)
1253+
}
1254+
12131255
func exampleLogsStaticTriggerCondition(triggerType string, threshold float64, thresholdType string) TriggerCondition {
12141256
return TriggerCondition{
12151257
TimeRange: "30m",
@@ -1348,3 +1390,25 @@ func testAccCheckMonitorsLibraryMonitorFGPBackend(
13481390
return nil
13491391
}
13501392
}
1393+
1394+
var allInvalidTriggerConditionMonitorResources = []func(testName string) string{
1395+
invalidExampleWithNoTriggerCondition,
1396+
invalidExampleWithEmptyLogStaticTriggerCondition,
1397+
invalidExampleWithEmptyMetricsStaticTriggerCondition,
1398+
}
1399+
1400+
func invalidExampleWithNoTriggerCondition(testName string) string {
1401+
query := "error | timeslice 1m | count as field by _timeslice"
1402+
return exampleMonitorWithTriggerCondition(testName, "Logs", query,
1403+
` `, []string{"Critical", "ResolvedCritical"})
1404+
}
1405+
func invalidExampleWithEmptyLogStaticTriggerCondition(testName string) string {
1406+
query := "error | timeslice 1m | count as field by _timeslice"
1407+
return exampleMonitorWithTriggerCondition(testName, "Logs", query,
1408+
`logs_static_condition {}`, []string{"Critical", "ResolvedCritical"})
1409+
}
1410+
func invalidExampleWithEmptyMetricsStaticTriggerCondition(testName string) string {
1411+
query := "error | timeslice 1m | count as field by _timeslice"
1412+
return exampleMonitorWithTriggerCondition(testName, "Logs", query,
1413+
`metrics_static_condition {}`, []string{"Critical", "ResolvedCritical"})
1414+
}

0 commit comments

Comments
 (0)