Skip to content

Commit 69618be

Browse files
authored
feat(alerts): add form based prometheus alert type (#396)
1 parent 76a475a commit 69618be

13 files changed

+712
-10
lines changed

sysdig/internal/client/v2/alerts_v2.go

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ const (
2222
labelsV3Path = "%s/api/v3/labels/?limit=6000"
2323
labelsV3DescriptorsPath = "%s/api/v3/labels/descriptors/%s"
2424

25-
AlertV2TypePrometheus AlertV2Type = "PROMETHEUS"
26-
AlertV2TypeManual AlertV2Type = "MANUAL"
27-
AlertV2TypeEvent AlertV2Type = "EVENT"
28-
AlertV2TypeChange AlertV2Type = "PERCENTAGE_OF_CHANGE"
25+
AlertV2TypePrometheus AlertV2Type = "PROMETHEUS"
26+
AlertV2TypeManual AlertV2Type = "MANUAL"
27+
AlertV2TypeEvent AlertV2Type = "EVENT"
28+
AlertV2TypeChange AlertV2Type = "PERCENTAGE_OF_CHANGE"
29+
AlertV2TypeFormBasedPrometheus AlertV2Type = "FORM_BASED_PROMETHEUS"
2930

3031
AlertV2SeverityHigh AlertV2Severity = "high"
3132
AlertV2SeverityMedium AlertV2Severity = "medium"
@@ -48,6 +49,7 @@ type AlertV2Interface interface {
4849
AlertV2MetricInterface
4950
AlertV2DowntimeInterface
5051
AlertV2ChangeInterface
52+
AlertV2FormBasedPrometheusInterface
5153
}
5254

5355
type AlertV2PrometheusInterface interface {
@@ -82,6 +84,14 @@ type AlertV2ChangeInterface interface {
8284
DeleteAlertV2Change(ctx context.Context, alertID int) error
8385
}
8486

87+
type AlertV2FormBasedPrometheusInterface interface {
88+
Base
89+
CreateAlertV2FormBasedPrometheus(ctx context.Context, alert AlertV2FormBasedPrometheus) (AlertV2FormBasedPrometheus, error)
90+
UpdateAlertV2FormBasedPrometheus(ctx context.Context, alert AlertV2FormBasedPrometheus) (AlertV2FormBasedPrometheus, error)
91+
GetAlertV2FormBasedPrometheus(ctx context.Context, alertID int) (AlertV2FormBasedPrometheus, error)
92+
DeleteAlertV2FormBasedPrometheus(ctx context.Context, alertID int) error
93+
}
94+
8595
type AlertV2DowntimeInterface interface {
8696
Base
8797
CreateAlertV2Downtime(ctx context.Context, alert AlertV2Downtime) (AlertV2Downtime, error)
@@ -459,6 +469,82 @@ func (client *Client) DeleteAlertV2Change(ctx context.Context, alertID int) erro
459469
return client.deleteAlertV2(ctx, alertID)
460470
}
461471

472+
func (client *Client) CreateAlertV2FormBasedPrometheus(ctx context.Context, alert AlertV2FormBasedPrometheus) (AlertV2FormBasedPrometheus, error) {
473+
err := client.addNotificationChannelType(ctx, alert.NotificationChannelConfigList)
474+
if err != nil {
475+
return AlertV2FormBasedPrometheus{}, err
476+
}
477+
478+
err = client.translateScopeSegmentLabels(ctx, &alert.Config.ScopedSegmentedConfig)
479+
if err != nil {
480+
return AlertV2FormBasedPrometheus{}, err
481+
}
482+
483+
payload, err := Marshal(alertV2FormBasedPrometheusWrapper{Alert: alert})
484+
if err != nil {
485+
return AlertV2FormBasedPrometheus{}, err
486+
}
487+
488+
body, err := client.createAlertV2(ctx, payload)
489+
if err != nil {
490+
return AlertV2FormBasedPrometheus{}, err
491+
}
492+
493+
wrapper, err := Unmarshal[alertV2FormBasedPrometheusWrapper](body)
494+
if err != nil {
495+
return AlertV2FormBasedPrometheus{}, err
496+
}
497+
498+
return wrapper.Alert, nil
499+
}
500+
501+
func (client *Client) UpdateAlertV2FormBasedPrometheus(ctx context.Context, alert AlertV2FormBasedPrometheus) (AlertV2FormBasedPrometheus, error) {
502+
err := client.addNotificationChannelType(ctx, alert.NotificationChannelConfigList)
503+
if err != nil {
504+
return AlertV2FormBasedPrometheus{}, err
505+
}
506+
507+
err = client.translateScopeSegmentLabels(ctx, &alert.Config.ScopedSegmentedConfig)
508+
if err != nil {
509+
return AlertV2FormBasedPrometheus{}, err
510+
}
511+
512+
payload, err := Marshal(alertV2FormBasedPrometheusWrapper{Alert: alert})
513+
if err != nil {
514+
return AlertV2FormBasedPrometheus{}, err
515+
}
516+
517+
body, err := client.updateAlertV2(ctx, alert.ID, payload)
518+
if err != nil {
519+
return AlertV2FormBasedPrometheus{}, err
520+
}
521+
522+
wrapper, err := Unmarshal[alertV2FormBasedPrometheusWrapper](body)
523+
if err != nil {
524+
return AlertV2FormBasedPrometheus{}, err
525+
}
526+
527+
return wrapper.Alert, nil
528+
}
529+
530+
func (client *Client) GetAlertV2FormBasedPrometheus(ctx context.Context, alertID int) (AlertV2FormBasedPrometheus, error) {
531+
body, err := client.getAlertV2(ctx, alertID)
532+
if err != nil {
533+
return AlertV2FormBasedPrometheus{}, err
534+
}
535+
536+
wrapper, err := Unmarshal[alertV2FormBasedPrometheusWrapper](body)
537+
if err != nil {
538+
return AlertV2FormBasedPrometheus{}, err
539+
}
540+
541+
return wrapper.Alert, nil
542+
}
543+
544+
func (client *Client) DeleteAlertV2FormBasedPrometheus(ctx context.Context, alertID int) error {
545+
return client.deleteAlertV2(ctx, alertID)
546+
}
547+
462548
func (client *Client) createAlertV2(ctx context.Context, alertJson io.Reader) (io.ReadCloser, error) {
463549
response, err := client.requester.Request(ctx, http.MethodPost, client.alertsV2URL(), alertJson)
464550
if err != nil {

sysdig/internal/client/v2/model.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,27 @@ type AlertV2ConfigChange struct {
643643
LongerRangeSec int `json:"longerRangeSec"`
644644
}
645645

646+
type AlertV2ConfigFormBasedPrometheus struct {
647+
ScopedSegmentedConfig
648+
649+
Query string `json:"query"`
650+
ConditionOperator string `json:"conditionOperator"`
651+
Threshold float64 `json:"threshold"`
652+
WarningConditionOperator string `json:"warningConditionOperator,omitempty"`
653+
WarningThreshold *float64 `json:"warningThreshold,omitempty"`
654+
NoDataBehaviour string `json:"noDataBehaviour"`
655+
}
656+
657+
type AlertV2FormBasedPrometheus struct {
658+
AlertV2Common
659+
DurationSec int `json:"durationSec"` // not really used but the api wants it set to 0 in POST/PUT
660+
Config AlertV2ConfigFormBasedPrometheus `json:"config"`
661+
}
662+
663+
type alertV2FormBasedPrometheusWrapper struct {
664+
Alert AlertV2FormBasedPrometheus `json:"alert"`
665+
}
666+
646667
type AlertV2Change struct {
647668
AlertV2Common
648669
DurationSec int `json:"durationSec"` // not really used but the api wants it set to 0 in POST/PUT

sysdig/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ func Provider() *schema.Provider {
146146
"sysdig_monitor_alert_v2_downtime": resourceSysdigMonitorAlertV2Downtime(),
147147
"sysdig_monitor_alert_v2_prometheus": resourceSysdigMonitorAlertV2Prometheus(),
148148
"sysdig_monitor_alert_v2_change": resourceSysdigMonitorAlertV2Change(),
149+
"sysdig_monitor_alert_v2_form_based_prometheus": resourceSysdigMonitorAlertV2FormBasedPrometheus(),
149150
"sysdig_monitor_dashboard": resourceSysdigMonitorDashboard(),
150151
"sysdig_monitor_notification_channel_email": resourceSysdigMonitorNotificationChannelEmail(),
151152
"sysdig_monitor_notification_channel_opsgenie": resourceSysdigMonitorNotificationChannelOpsGenie(),

sysdig/resource_sysdig_monitor_alert_v2_change_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ resource "sysdig_monitor_alert_v2_change" "sample" {
269269
longer_time_range_seconds = 3600
270270
link {
271271
type = "runbook"
272-
href = "http://ciao2.com"
272+
href = "http://example.com"
273273
}
274274
link {
275275
type = "dashboard"
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
package sysdig
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strconv"
7+
"time"
8+
9+
v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2"
10+
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
13+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
14+
)
15+
16+
func resourceSysdigMonitorAlertV2FormBasedPrometheus() *schema.Resource {
17+
18+
timeout := 5 * time.Minute
19+
20+
return &schema.Resource{
21+
CreateContext: resourceSysdigMonitorAlertV2FormBasedPrometheusCreate,
22+
UpdateContext: resourceSysdigMonitorAlertV2FormBasedPrometheusUpdate,
23+
ReadContext: resourceSysdigMonitorAlertV2FormBasedPrometheusRead,
24+
DeleteContext: resourceSysdigMonitorAlertV2FormBasedPrometheusDelete,
25+
Importer: &schema.ResourceImporter{
26+
StateContext: schema.ImportStatePassthroughContext,
27+
},
28+
29+
Timeouts: &schema.ResourceTimeout{
30+
Create: schema.DefaultTimeout(timeout),
31+
Update: schema.DefaultTimeout(timeout),
32+
Read: schema.DefaultTimeout(timeout),
33+
Delete: schema.DefaultTimeout(timeout),
34+
},
35+
36+
Schema: createScopedSegmentedAlertV2Schema(createAlertV2Schema(map[string]*schema.Schema{
37+
"operator": {
38+
Type: schema.TypeString,
39+
Required: true,
40+
ValidateFunc: validation.StringInSlice([]string{">", ">=", "<", "<=", "=", "!="}, false),
41+
},
42+
"threshold": {
43+
Type: schema.TypeFloat,
44+
Required: true,
45+
},
46+
"warning_threshold": {
47+
Type: schema.TypeString,
48+
Optional: true,
49+
Default: "",
50+
},
51+
"query": {
52+
Type: schema.TypeString,
53+
Required: true,
54+
},
55+
"no_data_behaviour": {
56+
Type: schema.TypeString,
57+
Optional: true,
58+
Default: "DO_NOTHING",
59+
ValidateFunc: validation.StringInSlice([]string{"DO_NOTHING", "TRIGGER"}, false),
60+
},
61+
})),
62+
}
63+
}
64+
65+
func getAlertV2FormBasedPrometheusClient(c SysdigClients) (v2.AlertV2FormBasedPrometheusInterface, error) {
66+
return getAlertV2Client(c)
67+
}
68+
69+
func resourceSysdigMonitorAlertV2FormBasedPrometheusCreate(ctx context.Context, d *schema.ResourceData, i interface{}) diag.Diagnostics {
70+
client, err := getAlertV2FormBasedPrometheusClient(i.(SysdigClients))
71+
if err != nil {
72+
return diag.FromErr(err)
73+
}
74+
75+
a, err := buildAlertV2FormBasedPrometheusStruct(d)
76+
if err != nil {
77+
return diag.FromErr(err)
78+
}
79+
80+
aCreated, err := client.CreateAlertV2FormBasedPrometheus(ctx, *a)
81+
if err != nil {
82+
return diag.FromErr(err)
83+
}
84+
85+
d.SetId(strconv.Itoa(aCreated.ID))
86+
87+
err = updateAlertV2FormBasedPrometheusState(d, &aCreated)
88+
if err != nil {
89+
return diag.FromErr(err)
90+
}
91+
92+
return nil
93+
}
94+
95+
func resourceSysdigMonitorAlertV2FormBasedPrometheusRead(ctx context.Context, d *schema.ResourceData, i interface{}) diag.Diagnostics {
96+
client, err := getAlertV2FormBasedPrometheusClient(i.(SysdigClients))
97+
if err != nil {
98+
return diag.FromErr(err)
99+
}
100+
101+
id, err := strconv.Atoi(d.Id())
102+
if err != nil {
103+
return diag.FromErr(err)
104+
}
105+
106+
a, err := client.GetAlertV2FormBasedPrometheus(ctx, id)
107+
if err != nil {
108+
if err == v2.AlertV2NotFound {
109+
d.SetId("")
110+
return nil
111+
}
112+
return diag.FromErr(err)
113+
}
114+
115+
err = updateAlertV2FormBasedPrometheusState(d, &a)
116+
if err != nil {
117+
return diag.FromErr(err)
118+
}
119+
120+
return nil
121+
}
122+
123+
func resourceSysdigMonitorAlertV2FormBasedPrometheusUpdate(ctx context.Context, d *schema.ResourceData, i interface{}) diag.Diagnostics {
124+
client, err := getAlertV2FormBasedPrometheusClient(i.(SysdigClients))
125+
if err != nil {
126+
return diag.FromErr(err)
127+
}
128+
129+
a, err := buildAlertV2FormBasedPrometheusStruct(d)
130+
if err != nil {
131+
return diag.FromErr(err)
132+
}
133+
134+
a.ID, _ = strconv.Atoi(d.Id())
135+
136+
aUpdated, err := client.UpdateAlertV2FormBasedPrometheus(ctx, *a)
137+
if err != nil {
138+
return diag.FromErr(err)
139+
}
140+
141+
err = updateAlertV2FormBasedPrometheusState(d, &aUpdated)
142+
if err != nil {
143+
return diag.FromErr(err)
144+
}
145+
146+
return nil
147+
}
148+
149+
func resourceSysdigMonitorAlertV2FormBasedPrometheusDelete(ctx context.Context, d *schema.ResourceData, i interface{}) diag.Diagnostics {
150+
client, err := getAlertV2FormBasedPrometheusClient(i.(SysdigClients))
151+
if err != nil {
152+
return diag.FromErr(err)
153+
}
154+
155+
id, err := strconv.Atoi(d.Id())
156+
if err != nil {
157+
return diag.FromErr(err)
158+
}
159+
160+
err = client.DeleteAlertV2FormBasedPrometheus(ctx, id)
161+
if err != nil {
162+
return diag.FromErr(err)
163+
}
164+
165+
return nil
166+
}
167+
168+
func buildAlertV2FormBasedPrometheusStruct(d *schema.ResourceData) (*v2.AlertV2FormBasedPrometheus, error) {
169+
alertV2Common := buildAlertV2CommonStruct(d)
170+
alertV2Common.Type = string(v2.AlertV2TypeFormBasedPrometheus)
171+
config := v2.AlertV2ConfigFormBasedPrometheus{}
172+
173+
buildScopedSegmentedConfigStruct(d, &config.ScopedSegmentedConfig)
174+
175+
//ConditionOperator
176+
config.ConditionOperator = d.Get("operator").(string)
177+
178+
//threshold
179+
config.Threshold = d.Get("threshold").(float64)
180+
181+
//WarningThreshold
182+
if warningThreshold, ok := d.GetOk("warning_threshold"); ok {
183+
wts := warningThreshold.(string)
184+
wt, err := strconv.ParseFloat(wts, 64)
185+
if err != nil {
186+
return nil, fmt.Errorf("cannot convert warning_threshold to a number: %w", err)
187+
}
188+
config.WarningThreshold = &wt
189+
config.WarningConditionOperator = config.ConditionOperator
190+
}
191+
192+
//Query
193+
config.Query = d.Get("query").(string)
194+
195+
config.NoDataBehaviour = d.Get("no_data_behaviour").(string)
196+
197+
alert := &v2.AlertV2FormBasedPrometheus{
198+
AlertV2Common: *alertV2Common,
199+
DurationSec: 0,
200+
Config: config,
201+
}
202+
return alert, nil
203+
}
204+
205+
func updateAlertV2FormBasedPrometheusState(d *schema.ResourceData, alert *v2.AlertV2FormBasedPrometheus) error {
206+
err := updateAlertV2CommonState(d, &alert.AlertV2Common)
207+
if err != nil {
208+
return err
209+
}
210+
211+
err = updateScopedSegmentedConfigState(d, &alert.Config.ScopedSegmentedConfig)
212+
if err != nil {
213+
return err
214+
}
215+
216+
_ = d.Set("operator", alert.Config.ConditionOperator)
217+
218+
_ = d.Set("threshold", alert.Config.Threshold)
219+
220+
if alert.Config.WarningThreshold != nil {
221+
_ = d.Set("warning_threshold", fmt.Sprintf("%v", *alert.Config.WarningThreshold))
222+
}
223+
224+
_ = d.Set("query", alert.Config.Query)
225+
226+
_ = d.Set("no_data_behaviour", alert.Config.NoDataBehaviour)
227+
228+
return nil
229+
}

0 commit comments

Comments
 (0)