Skip to content

Commit

Permalink
feat(alerts): introduce duration attribute (#525)
Browse files Browse the repository at this point in the history
* chore(alerts): use new duration fields for v1 alerts

* feat(alerts): introduce duration attribute
  • Loading branch information
dbonf authored Aug 9, 2024
1 parent 663d0d1 commit 2158560
Show file tree
Hide file tree
Showing 24 changed files with 358 additions and 98 deletions.
27 changes: 17 additions & 10 deletions sysdig/internal/client/v2/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ type Alert struct {
NotificationChannelIds []int `json:"notificationChannelIds"`
Filter string `json:"filter"`
Severity int `json:"severity"`
Timespan int `json:"timespan"`
Timespan *int `json:"timespan,omitempty"`
Duration *int `json:"duration,omitempty"`
CustomNotification *CustomNotification `json:"customNotification"`
TeamID int `json:"teamId,omitempty"`
AutoCreated bool `json:"autoCreated"`
Expand Down Expand Up @@ -712,12 +713,13 @@ type AlertV2Common struct {
type AlertV2ConfigPrometheus struct {
Query string `json:"query"`
KeepFiringForSec *int `json:"keepFiringForSec,omitempty"`

Duration int `json:"duration"`
}

type AlertV2Prometheus struct {
AlertV2Common
DurationSec int `json:"durationSec"`
Config AlertV2ConfigPrometheus `json:"config"`
Config AlertV2ConfigPrometheus `json:"config"`
}

type alertV2PrometheusWrapper struct {
Expand Down Expand Up @@ -755,12 +757,13 @@ type AlertV2ConfigEvent struct {

Filter string `json:"filter"`
Tags []string `json:"tags"`

Range int `json:"range"`
}

type AlertV2Event struct {
AlertV2Common
DurationSec int `json:"durationSec"`
Config AlertV2ConfigEvent `json:"config"`
Config AlertV2ConfigEvent `json:"config"`
}

type alertV2EventWrapper struct {
Expand Down Expand Up @@ -796,11 +799,13 @@ type AlertV2ConfigMetric struct {
TimeAggregation string `json:"timeAggregation"`
Metric AlertMetricDescriptorV2 `json:"metric"`
NoDataBehaviour string `json:"noDataBehaviour"`

Range int `json:"range"`
Duration int `json:"duration"`
}

type AlertV2Metric struct {
AlertV2Common
DurationSec int `json:"durationSec"`
Config AlertV2ConfigMetric `json:"config"`
UnreportedAlertNotificationsRetentionSec *int `json:"unreportedAlertNotificationsRetentionSec"`
}
Expand All @@ -818,11 +823,12 @@ type AlertV2ConfigDowntime struct {
GroupAggregation string `json:"groupAggregation"`
TimeAggregation string `json:"timeAggregation"`
Metric AlertMetricDescriptorV2 `json:"metric"`

Range int `json:"range"`
}

type AlertV2Downtime struct {
AlertV2Common
DurationSec int `json:"durationSec"`
Config AlertV2ConfigDowntime `json:"config"`
UnreportedAlertNotificationsRetentionSec *int `json:"unreportedAlertNotificationsRetentionSec"`
}
Expand Down Expand Up @@ -856,11 +862,12 @@ type AlertV2ConfigFormBasedPrometheus struct {
WarningConditionOperator string `json:"warningConditionOperator,omitempty"`
WarningThreshold *float64 `json:"warningThreshold,omitempty"`
NoDataBehaviour string `json:"noDataBehaviour"`

Duration int `json:"duration"`
}

type AlertV2FormBasedPrometheus struct {
AlertV2Common
DurationSec int `json:"durationSec"` // not really used but the api wants it set to 0 in POST/PUT
Config AlertV2ConfigFormBasedPrometheus `json:"config"`
UnreportedAlertNotificationsRetentionSec *int `json:"unreportedAlertNotificationsRetentionSec"`
}
Expand All @@ -881,11 +888,12 @@ type AlertV2ConfigGroupOutlier struct {
TimeAggregation string `json:"timeAggregation"`
Metric AlertMetricDescriptorV2 `json:"metric"`
NoDataBehaviour string `json:"noDataBehaviour"`

ObservationWindow int `json:"observationWindow"`
}

type AlertV2GroupOutlier struct {
AlertV2Common
DurationSec int `json:"durationSec"` // Observation window should be greater than or equal to 10 minutes
Config AlertV2ConfigGroupOutlier `json:"config"`
UnreportedAlertNotificationsRetentionSec *int `json:"unreportedAlertNotificationsRetentionSec"`
}
Expand All @@ -896,7 +904,6 @@ type alertV2GroupOutlierWrapper struct {

type AlertV2Change struct {
AlertV2Common
DurationSec int `json:"durationSec"` // not really used but the api wants it set to 0 in POST/PUT
Config AlertV2ConfigChange `json:"config"`
UnreportedAlertNotificationsRetentionSec *int `json:"unreportedAlertNotificationsRetentionSec"`
}
Expand Down
10 changes: 7 additions & 3 deletions sysdig/resource_sysdig_monitor_alert_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,11 @@ func createAlertSchema(original map[string]*schema.Schema) map[string]*schema.Sc

func alertFromResourceData(d *schema.ResourceData) (alert *v2.Alert, err error) {
trigger_after_minutes := time.Duration(d.Get("trigger_after_minutes").(int)) * time.Minute
timespan := int(trigger_after_minutes.Microseconds())
alert = &v2.Alert{
Name: d.Get("name").(string),
Type: "MANUAL",
Timespan: int(trigger_after_minutes.Microseconds()),
Timespan: &timespan,
SegmentBy: []string{},
NotificationChannelIds: []int{},
CustomNotification: &v2.CustomNotification{
Expand Down Expand Up @@ -202,13 +203,16 @@ func alertFromResourceData(d *schema.ResourceData) (alert *v2.Alert, err error)
}

func alertToResourceData(alert *v2.Alert, data *schema.ResourceData) (err error) {
trigger_after_minutes := time.Duration(alert.Timespan) * time.Microsecond
var trigger_after_minutes int
if alert.Timespan != nil {
trigger_after_minutes = int((time.Duration(*alert.Timespan) * time.Microsecond).Minutes())
}

_ = data.Set("version", alert.Version)
_ = data.Set("name", alert.Name)
_ = data.Set("description", alert.Description)
_ = data.Set("scope", alert.Filter)
_ = data.Set("trigger_after_minutes", int(trigger_after_minutes.Minutes()))
_ = data.Set("trigger_after_minutes", trigger_after_minutes)
_ = data.Set("group_name", alert.GroupName)
_ = data.Set("team", alert.TeamID)
_ = data.Set("enabled", alert.Enabled)
Expand Down
8 changes: 8 additions & 0 deletions sysdig/resource_sysdig_monitor_alert_promql.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ func promqlAlertFromResourceData(data *schema.ResourceData) (alert *v2.Alert, er
if err != nil {
return
}
duration := int((time.Duration(*alert.Timespan) * time.Microsecond).Seconds())
alert.Duration = &duration
alert.Timespan = nil

alert.Type = "PROMETHEUS"

Expand All @@ -145,6 +148,11 @@ func promqlAlertToResourceData(alert *v2.Alert, data *schema.ResourceData) (err
return
}

if alert.Duration != nil {
trigger_after_minutes := int((time.Duration(*alert.Duration) * time.Second).Minutes())
_ = data.Set("trigger_after_minutes", trigger_after_minutes)
}

_ = data.Set("promql", alert.Condition)

return
Expand Down
1 change: 0 additions & 1 deletion sysdig/resource_sysdig_monitor_alert_v2_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,6 @@ func buildAlertV2ChangeStruct(d *schema.ResourceData) (*v2.AlertV2Change, error)

alert := &v2.AlertV2Change{
AlertV2Common: *alertV2Common,
DurationSec: 0,
Config: config,
UnreportedAlertNotificationsRetentionSec: unreportedAlertNotificationsRetentionSec,
}
Expand Down
31 changes: 27 additions & 4 deletions sysdig/resource_sysdig_monitor_alert_v2_downtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,18 @@ func resourceSysdigMonitorAlertV2Downtime() *schema.Resource {

Schema: createScopedSegmentedAlertV2Schema(createAlertV2Schema(map[string]*schema.Schema{
"trigger_after_minutes": {
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Optional: true,
Computed: true, // computed if range_seconds is defined
Deprecated: "Use range_seconds instead",
ValidateFunc: validation.IntAtLeast(1),
},
"range_seconds": {
Type: schema.TypeInt,
Optional: true,
Computed: true, // computed if trigger_after_minutes is defined
ExactlyOneOf: []string{"trigger_after_minutes"},
ValidateFunc: validation.IntAtLeast(60),
},
"threshold": {
Type: schema.TypeFloat,
Expand Down Expand Up @@ -175,6 +185,19 @@ func buildAlertV2DowntimeStruct(d *schema.ResourceData) *v2.AlertV2Downtime {
metric := d.Get("metric").(string)
config.Metric.ID = metric

if attr, ok := d.GetOk("range_seconds"); ok && attr != nil {
config.Range = d.Get("range_seconds").(int)
}

if d.HasChange("trigger_after_minutes") {
// GetOk returns true even if the value is stored only in the state and not in the user config:
// to avoid applying a trigger_after_minutes old value from the state even if the user removed it from the config
// we use HasChange that is true only if the use has changed (or created) it - and so it must be in the config
if attr, ok := d.GetOk("trigger_after_minutes"); ok && attr != nil {
config.Range = minutesToSeconds(d.Get("trigger_after_minutes").(int))
}
}

var unreportedAlertNotificationsRetentionSec *int
if unreportedAlertNotificationsRetentionSecInterface, ok := d.GetOk("unreported_alert_notifications_retention_seconds"); ok {
u := unreportedAlertNotificationsRetentionSecInterface.(int)
Expand All @@ -183,7 +206,6 @@ func buildAlertV2DowntimeStruct(d *schema.ResourceData) *v2.AlertV2Downtime {

alert := &v2.AlertV2Downtime{
AlertV2Common: *alertV2Common,
DurationSec: minutesToSeconds(d.Get("trigger_after_minutes").(int)),
Config: config,
UnreportedAlertNotificationsRetentionSec: unreportedAlertNotificationsRetentionSec,
}
Expand All @@ -201,7 +223,8 @@ func updateAlertV2DowntimeState(d *schema.ResourceData, alert *v2.AlertV2Downtim
return err
}

_ = d.Set("trigger_after_minutes", secondsToMinutes(alert.DurationSec))
_ = d.Set("trigger_after_minutes", secondsToMinutes(alert.Config.Range))
_ = d.Set("range_seconds", alert.Config.Range)

_ = d.Set("threshold", (1-alert.Config.Threshold)*100)

Expand Down
28 changes: 26 additions & 2 deletions sysdig/resource_sysdig_monitor_alert_v2_downtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ func TestAccAlertV2Downtime(t *testing.T) {
{
Config: alertV2DowntimeWithName(rText()),
},
{
Config: alertV2DowntimeWithTriggerAfterMinutes(rText()),
},
{
Config: alertV2DowntimeWithWithUnreportedAlertNotificationsRetentionSec(rText()),
},
Expand Down Expand Up @@ -56,6 +59,27 @@ resource "sysdig_monitor_alert_v2_downtime" "sample" {
values = ["thom-cluster1", "demo-env-prom"]
}
range_seconds = 600
}
`, name)
}

func alertV2DowntimeWithTriggerAfterMinutes(name string) string {
return fmt.Sprintf(`
resource "sysdig_monitor_alert_v2_downtime" "sample" {
name = "TERRAFORM TEST - DOWNTIMEV2 %s"
metric = "sysdig_container_up"
threshold = 75
scope {
label = "kube_cluster_name"
operator = "in"
values = ["thom-cluster1", "demo-env-prom"]
}
trigger_after_minutes = 15
}
Expand All @@ -77,7 +101,7 @@ resource "sysdig_monitor_alert_v2_downtime" "sample" {
values = ["thom-cluster1", "demo-env-prom"]
}
trigger_after_minutes = 15
range_seconds = 600
unreported_alert_notifications_retention_seconds = 60 * 60 * 24 * 30
}
Expand All @@ -100,7 +124,7 @@ func alertV2DowntimeWithGroupBy(name string) string {
values = ["thom-cluster1", "demo-env-prom"]
}
trigger_after_minutes = 15
range_seconds = 600
}
Expand Down
31 changes: 27 additions & 4 deletions sysdig/resource_sysdig_monitor_alert_v2_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,18 @@ func resourceSysdigMonitorAlertV2Event() *schema.Resource {

Schema: createScopedSegmentedAlertV2Schema(createAlertV2Schema(map[string]*schema.Schema{
"trigger_after_minutes": {
Type: schema.TypeInt,
Required: true,
Type: schema.TypeInt,
Optional: true,
Computed: true, // computed if range_seconds is defined
Deprecated: "Use range_seconds instead",
ValidateFunc: validation.IntAtLeast(1),
},
"range_seconds": {
Type: schema.TypeInt,
Optional: true,
Computed: true, // computed if trigger_after_minutes is defined
ExactlyOneOf: []string{"trigger_after_minutes"},
ValidateFunc: validation.IntAtLeast(60),
},
"operator": {
Type: schema.TypeString,
Expand Down Expand Up @@ -203,9 +213,21 @@ func buildAlertV2EventStruct(d *schema.ResourceData) (*v2.AlertV2Event, error) {
}
config.Tags = tags

if attr, ok := d.GetOk("range_seconds"); ok && attr != nil {
config.Range = d.Get("range_seconds").(int)
}

if d.HasChange("trigger_after_minutes") {
// GetOk returns true even if the value is stored only in the state and not in the user config:
// to avoid applying a trigger_after_minutes old value from the state even if the user removed it from the config
// we use HasChange that is true only if the use has changed (or created) it - and so it must be in the config
if attr, ok := d.GetOk("trigger_after_minutes"); ok && attr != nil {
config.Range = minutesToSeconds(d.Get("trigger_after_minutes").(int))
}
}

alert := &v2.AlertV2Event{
AlertV2Common: *alertV2Common,
DurationSec: minutesToSeconds(d.Get("trigger_after_minutes").(int)),
Config: config,
}
return alert, nil
Expand All @@ -222,7 +244,8 @@ func updateAlertV2EventState(d *schema.ResourceData, alert *v2.AlertV2Event) err
return err
}

_ = d.Set("trigger_after_minutes", secondsToMinutes(alert.DurationSec))
_ = d.Set("trigger_after_minutes", secondsToMinutes(alert.Config.Range))
_ = d.Set("range_seconds", alert.Config.Range)

_ = d.Set("operator", alert.Config.ConditionOperator)

Expand Down
Loading

0 comments on commit 2158560

Please sign in to comment.