Skip to content

Commit c1febe0

Browse files
authored
Merge pull request #363 from SumoLogic/agrv-slo-monitors
[Monitors] slo monitors support
2 parents 27b85a5 + 3f5f32d commit c1febe0

File tree

3 files changed

+289
-38
lines changed

3 files changed

+289
-38
lines changed

sumologic/resource_sumologic_monitors_library_monitor.go

Lines changed: 189 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,12 @@ func resourceSumologicMonitorsLibraryMonitor() *schema.Resource {
120120
ValidateFunc: validation.StringInSlice([]string{"AtLeastOnce", "Always", "ResultCount", "MissingData"}, false),
121121
},
122122
"detection_method": {
123-
Type: schema.TypeString,
124-
Optional: true,
125-
ValidateFunc: validation.StringInSlice([]string{"StaticCondition", "LogsStaticCondition", "MetricsStaticCondition", "LogsOutlierCondition", "MetricsOutlierCondition", "LogsMissingDataCondition", "MetricsMissingDataCondition"}, false),
123+
Type: schema.TypeString,
124+
Optional: true,
125+
ValidateFunc: validation.StringInSlice([]string{"StaticCondition", "LogsStaticCondition",
126+
"MetricsStaticCondition", "LogsOutlierCondition", "MetricsOutlierCondition",
127+
"LogsMissingDataCondition", "MetricsMissingDataCondition", "SloSliCondition",
128+
"SloBurnRateCondition"}, false),
126129
},
127130
},
128131
},
@@ -182,6 +185,22 @@ func resourceSumologicMonitorsLibraryMonitor() *schema.Resource {
182185
Schema: metricsMissingDataTriggerConditionSchema,
183186
},
184187
},
188+
sloSLIConditionFieldName: {
189+
Type: schema.TypeList,
190+
MaxItems: 1,
191+
Optional: true,
192+
Elem: &schema.Resource{
193+
Schema: sloSLITriggerConditionSchema,
194+
},
195+
},
196+
sloBurnRateConditionFieldName: {
197+
Type: schema.TypeList,
198+
MaxItems: 1,
199+
Optional: true,
200+
Elem: &schema.Resource{
201+
Schema: sloBurnRateTriggerConditionSchema,
202+
},
203+
},
185204
},
186205
},
187206
},
@@ -266,7 +285,7 @@ func resourceSumologicMonitorsLibraryMonitor() *schema.Resource {
266285
"monitor_type": {
267286
Type: schema.TypeString,
268287
Required: true,
269-
ValidateFunc: validation.StringInSlice([]string{"Logs", "Metrics"}, false),
288+
ValidateFunc: validation.StringInSlice([]string{"Logs", "Metrics", "Slo"}, false),
270289
},
271290

272291
"evaluation_delay": {
@@ -325,7 +344,10 @@ func resourceSumologicMonitorsLibraryMonitor() *schema.Resource {
325344
Type: schema.TypeString,
326345
Optional: true,
327346
},
328-
347+
"slo_id": {
348+
Type: schema.TypeString,
349+
Optional: true,
350+
},
329351
"alert_name": {
330352
Type: schema.TypeString,
331353
Optional: true,
@@ -445,6 +467,42 @@ var metricsMissingDataTriggerConditionSchema = map[string]*schema.Schema{
445467
},
446468
}
447469

470+
var sloSLITriggerConditionSchema = map[string]*schema.Schema{
471+
"critical": nested(true, schemaMap{
472+
"sli_threshold": {
473+
Type: schema.TypeFloat,
474+
Required: true,
475+
ValidateFunc: validation.FloatBetween(0, 100),
476+
},
477+
}),
478+
"warning": nested(true, schemaMap{
479+
"sli_threshold": {
480+
Type: schema.TypeFloat,
481+
Required: true,
482+
ValidateFunc: validation.FloatBetween(0, 100),
483+
},
484+
}),
485+
}
486+
487+
var sloBurnRateTriggerConditionSchema = map[string]*schema.Schema{
488+
"critical": nested(true, schemaMap{
489+
"time_range": &timeRangeSchema,
490+
"burn_rate_threshold": {
491+
Type: schema.TypeFloat,
492+
Required: true,
493+
ValidateFunc: validation.FloatAtLeast(0),
494+
},
495+
}),
496+
"warning": nested(true, schemaMap{
497+
"time_range": &timeRangeSchema,
498+
"burn_rate_threshold": {
499+
Type: schema.TypeFloat,
500+
Required: true,
501+
ValidateFunc: validation.FloatAtLeast(0),
502+
},
503+
}),
504+
}
505+
448506
var occurrenceTypeSchema = schema.Schema{
449507
Type: schema.TypeString,
450508
Required: true,
@@ -549,6 +607,7 @@ func resourceSumologicMonitorsLibraryMonitorRead(d *schema.ResourceData, meta in
549607
d.Set("group_notifications", monitor.GroupNotifications)
550608
d.Set("playbook", monitor.Playbook)
551609
d.Set("alert_name", monitor.AlertName)
610+
d.Set("slo_id", monitor.SloID)
552611
// set notifications
553612
notifications := make([]interface{}, len(monitor.Notifications))
554613
for i, n := range monitor.Notifications {
@@ -765,6 +824,13 @@ func triggerConditionsBlockToJson(block map[string]interface{}) []TriggerConditi
765824
if sc, ok := fromSingletonArray(block, metricsMissingDataConditionFieldName); ok {
766825
conditions = append(conditions, metricsMissingDataConditionBlockToJson(sc)...)
767826
}
827+
if sc, ok := fromSingletonArray(block, sloSLIConditionFieldName); ok {
828+
conditions = append(conditions, sloSLIConditionBlockToJson(sc)...)
829+
}
830+
if sc, ok := fromSingletonArray(block, sloBurnRateConditionFieldName); ok {
831+
conditions = append(conditions, sloBurnConditionBlockToJson(sc)...)
832+
}
833+
768834
return conditions
769835
}
770836

@@ -846,6 +912,7 @@ func metricsMissingDataConditionBlockToJson(block map[string]interface{}) []Trig
846912
DetectionMethod: metricsMissingDataConditionDetectionMethod,
847913
TriggerType: "MissingData",
848914
}
915+
849916
resolution := TriggerCondition{
850917
TimeRange: block["time_range"].(string),
851918
TriggerSource: block["trigger_source"].(string),
@@ -855,6 +922,22 @@ func metricsMissingDataConditionBlockToJson(block map[string]interface{}) []Trig
855922
return []TriggerCondition{alert, resolution}
856923
}
857924

925+
func sloSLIConditionBlockToJson(block map[string]interface{}) []TriggerCondition {
926+
base := TriggerCondition{
927+
DetectionMethod: sloSLIConditionDetectionMethod,
928+
}
929+
930+
return base.sloCloneReadingFromNestedBlocks(block)
931+
}
932+
933+
func sloBurnConditionBlockToJson(block map[string]interface{}) []TriggerCondition {
934+
base := TriggerCondition{
935+
DetectionMethod: sloBurnRateConditionDetectionMethod,
936+
}
937+
938+
return base.sloCloneReadingFromNestedBlocks(block)
939+
}
940+
858941
// TriggerCondition JSON model to 'trigger_conditions' block
859942
func jsonToTriggerConditionsBlock(conditions []TriggerCondition) map[string]interface{} {
860943
missingDataConditions := make([]TriggerCondition, 0)
@@ -877,6 +960,10 @@ func jsonToTriggerConditionsBlock(conditions []TriggerCondition) map[string]inte
877960
triggerConditionsBlock[logsOutlierConditionFieldName] = toSingletonArray(jsonToLogsOutlierConditionBlock(dataConditions))
878961
case metricsOutlierConditionDetectionMethod:
879962
triggerConditionsBlock[metricsOutlierConditionFieldName] = toSingletonArray(jsonToMetricsOutlierConditionBlock(dataConditions))
963+
case sloSLIConditionDetectionMethod:
964+
triggerConditionsBlock[sloSLIConditionFieldName] = toSingletonArray(jsonToSloSliConditionBlock(dataConditions))
965+
case sloBurnRateConditionDetectionMethod:
966+
triggerConditionsBlock[sloBurnRateConditionFieldName] = toSingletonArray(jsonToSloBurnRateConditionBlock(dataConditions))
880967
}
881968
}
882969
if len(missingDataConditions) > 0 {
@@ -1072,6 +1159,63 @@ func jsonToMetricsOutlierConditionBlock(conditions []TriggerCondition) map[strin
10721159
return block
10731160
}
10741161

1162+
func jsonToSloSliConditionBlock(conditions []TriggerCondition) map[string]interface{} {
1163+
var criticalAlrt, warningAlrt = dict{}, dict{}
1164+
block := map[string]interface{}{}
1165+
1166+
block["critical"] = toSingletonArray(criticalAlrt)
1167+
block["warning"] = toSingletonArray(warningAlrt)
1168+
1169+
var hasCritical, hasWarning = false, false
1170+
for _, condition := range conditions {
1171+
switch condition.TriggerType {
1172+
case "Critical":
1173+
hasCritical = true
1174+
criticalAlrt["sli_threshold"] = condition.SLIThreshold
1175+
case "Warning":
1176+
hasWarning = true
1177+
warningAlrt["sli_threshold"] = condition.SLIThreshold
1178+
}
1179+
}
1180+
if !hasCritical {
1181+
delete(block, "critical")
1182+
}
1183+
if !hasWarning {
1184+
delete(block, "warning")
1185+
}
1186+
return block
1187+
}
1188+
1189+
func jsonToSloBurnRateConditionBlock(conditions []TriggerCondition) map[string]interface{} {
1190+
1191+
var criticalAlrt, warningAlrt = dict{}, dict{}
1192+
block := map[string]interface{}{}
1193+
1194+
block["critical"] = toSingletonArray(criticalAlrt)
1195+
block["warning"] = toSingletonArray(warningAlrt)
1196+
1197+
var hasCritical, hasWarning = false, false
1198+
for _, condition := range conditions {
1199+
switch condition.TriggerType {
1200+
case "Critical":
1201+
hasCritical = true
1202+
criticalAlrt["time_range"] = condition.TimeRange
1203+
criticalAlrt["burn_rate_threshold"] = condition.BurnRateThreshold
1204+
case "Warning":
1205+
hasWarning = true
1206+
warningAlrt["time_range"] = condition.TimeRange
1207+
warningAlrt["burn_rate_threshold"] = condition.BurnRateThreshold
1208+
}
1209+
}
1210+
if !hasCritical {
1211+
delete(block, "critical")
1212+
}
1213+
if !hasWarning {
1214+
delete(block, "warning")
1215+
}
1216+
return block
1217+
}
1218+
10751219
func jsonToLogsMissingDataConditionBlock(conditions []TriggerCondition) map[string]interface{} {
10761220
block := map[string]interface{}{}
10771221
firstCondition := conditions[0]
@@ -1087,19 +1231,23 @@ func jsonToMetricsMissingDataConditionBlock(conditions []TriggerCondition) map[s
10871231
return block
10881232
}
10891233

1090-
var logsStaticConditionFieldName = "logs_static_condition"
1091-
var metricsStaticConditionFieldName = "metrics_static_condition"
1092-
var logsOutlierConditionFieldName = "logs_outlier_condition"
1093-
var metricsOutlierConditionFieldName = "metrics_outlier_condition"
1094-
var logsMissingDataConditionFieldName = "logs_missing_data_condition"
1095-
var metricsMissingDataConditionFieldName = "metrics_missing_data_condition"
1096-
1097-
var logsStaticConditionDetectionMethod = "LogsStaticCondition"
1098-
var metricsStaticConditionDetectionMethod = "MetricsStaticCondition"
1099-
var logsOutlierConditionDetectionMethod = "LogsOutlierCondition"
1100-
var metricsOutlierConditionDetectionMethod = "MetricsOutlierCondition"
1101-
var logsMissingDataConditionDetectionMethod = "LogsMissingDataCondition"
1102-
var metricsMissingDataConditionDetectionMethod = "MetricsMissingDataCondition"
1234+
const logsStaticConditionFieldName = "logs_static_condition"
1235+
const metricsStaticConditionFieldName = "metrics_static_condition"
1236+
const logsOutlierConditionFieldName = "logs_outlier_condition"
1237+
const metricsOutlierConditionFieldName = "metrics_outlier_condition"
1238+
const logsMissingDataConditionFieldName = "logs_missing_data_condition"
1239+
const metricsMissingDataConditionFieldName = "metrics_missing_data_condition"
1240+
const sloSLIConditionFieldName = "slo_sli_condition"
1241+
const sloBurnRateConditionFieldName = "slo_burn_rate_condition"
1242+
1243+
const logsStaticConditionDetectionMethod = "LogsStaticCondition"
1244+
const metricsStaticConditionDetectionMethod = "MetricsStaticCondition"
1245+
const logsOutlierConditionDetectionMethod = "LogsOutlierCondition"
1246+
const metricsOutlierConditionDetectionMethod = "MetricsOutlierCondition"
1247+
const logsMissingDataConditionDetectionMethod = "LogsMissingDataCondition"
1248+
const metricsMissingDataConditionDetectionMethod = "MetricsMissingDataCondition"
1249+
const sloSLIConditionDetectionMethod = "SloSliCondition"
1250+
const sloBurnRateConditionDetectionMethod = "SloBurnRateCondition"
11031251

11041252
func getQueries(d *schema.ResourceData) []MonitorQuery {
11051253
rawQueries := d.Get("queries").([]interface{})
@@ -1149,6 +1297,7 @@ func resourceToMonitorsLibraryMonitor(d *schema.ResourceData) MonitorsLibraryMon
11491297
GroupNotifications: d.Get("group_notifications").(bool),
11501298
Playbook: d.Get("playbook").(string),
11511299
AlertName: d.Get("alert_name").(string),
1300+
SloID: d.Get("slo_id").(string),
11521301
}
11531302
}
11541303

@@ -1212,6 +1361,10 @@ func (condition *TriggerCondition) readFrom(block map[string]interface{}) {
12121361
condition.Consecutive = v.(int)
12131362
case "direction":
12141363
condition.Direction = v.(string)
1364+
case "sli_threshold":
1365+
condition.SLIThreshold = v.(float64)
1366+
case "burn_rate_threshold":
1367+
condition.BurnRateThreshold = v.(float64)
12151368
default:
12161369
}
12171370
}
@@ -1269,6 +1422,24 @@ func (base TriggerCondition) cloneReadingFromNestedBlocks(block map[string]inter
12691422
return conditions
12701423
}
12711424

1425+
// adapted version of cloneReadingFromNestedBlocks for slo conditions
1426+
func (base TriggerCondition) sloCloneReadingFromNestedBlocks(block map[string]interface{}) []TriggerCondition {
1427+
var conditions = []TriggerCondition{}
1428+
var criticalCondition, warningCondition = base, base
1429+
criticalCondition.TriggerType = "Critical"
1430+
warningCondition.TriggerType = "Warning"
1431+
1432+
if critical, ok := fromSingletonArray(block, "critical"); ok {
1433+
criticalCondition.readFrom(critical)
1434+
conditions = append(conditions, criticalCondition)
1435+
}
1436+
if warning, ok := fromSingletonArray(block, "warning"); ok {
1437+
warningCondition.readFrom(warning)
1438+
conditions = append(conditions, warningCondition)
1439+
}
1440+
return conditions
1441+
}
1442+
12721443
func toSingletonArray(m map[string]interface{}) []map[string]interface{} {
12731444
return []map[string]interface{}{m}
12741445
}

sumologic/sumologic_monitors_library_monitor.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ type MonitorsLibraryMonitor struct {
139139
GroupNotifications bool `json:"groupNotifications"`
140140
Playbook string `json:"playbook,omitempty"`
141141
AlertName string `json:"alertName,omitempty"`
142+
SloID string `json:"sloId,omitempty"`
142143
}
143144

144145
type MonitorQuery struct {
@@ -147,18 +148,20 @@ type MonitorQuery struct {
147148
}
148149

149150
type TriggerCondition struct {
150-
TimeRange string `json:"timeRange"`
151-
TriggerType string `json:"triggerType"`
152-
Threshold float64 `json:"threshold,omitempty"`
153-
ThresholdType string `json:"thresholdType,omitempty"`
154-
OccurrenceType string `json:"occurrenceType"`
155-
TriggerSource string `json:"triggerSource"`
156-
DetectionMethod string `json:"detectionMethod"`
157-
Field string `json:"field,omitempty"`
158-
Window int `json:"window,omitempty"`
159-
BaselineWindow string `json:"baselineWindow,omitempty"`
160-
Consecutive int `json:"consecutive,omitempty"`
161-
Direction string `json:"direction,omitempty"`
151+
TimeRange string `json:"timeRange"`
152+
TriggerType string `json:"triggerType"`
153+
Threshold float64 `json:"threshold,omitempty"`
154+
ThresholdType string `json:"thresholdType,omitempty"`
155+
OccurrenceType string `json:"occurrenceType"`
156+
TriggerSource string `json:"triggerSource"`
157+
DetectionMethod string `json:"detectionMethod"`
158+
Field string `json:"field,omitempty"`
159+
Window int `json:"window,omitempty"`
160+
BaselineWindow string `json:"baselineWindow,omitempty"`
161+
Consecutive int `json:"consecutive,omitempty"`
162+
Direction string `json:"direction,omitempty"`
163+
SLIThreshold float64 `json:"sliThreshold,omitempty"`
164+
BurnRateThreshold float64 `json:"burnRateThreshold,omitempty"`
162165
}
163166

164167
type MonitorNotification struct {

0 commit comments

Comments
 (0)