Skip to content

Commit

Permalink
feat(alerts): add form based prometheus alert type
Browse files Browse the repository at this point in the history
  • Loading branch information
dbonf committed Aug 21, 2023
1 parent 76a475a commit f4938d2
Show file tree
Hide file tree
Showing 13 changed files with 712 additions and 10 deletions.
94 changes: 90 additions & 4 deletions sysdig/internal/client/v2/alerts_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ const (
labelsV3Path = "%s/api/v3/labels/?limit=6000"
labelsV3DescriptorsPath = "%s/api/v3/labels/descriptors/%s"

AlertV2TypePrometheus AlertV2Type = "PROMETHEUS"
AlertV2TypeManual AlertV2Type = "MANUAL"
AlertV2TypeEvent AlertV2Type = "EVENT"
AlertV2TypeChange AlertV2Type = "PERCENTAGE_OF_CHANGE"
AlertV2TypePrometheus AlertV2Type = "PROMETHEUS"
AlertV2TypeManual AlertV2Type = "MANUAL"
AlertV2TypeEvent AlertV2Type = "EVENT"
AlertV2TypeChange AlertV2Type = "PERCENTAGE_OF_CHANGE"
AlertV2TypeFormBasedPrometheus AlertV2Type = "FORM_BASED_PROMETHEUS"

AlertV2SeverityHigh AlertV2Severity = "high"
AlertV2SeverityMedium AlertV2Severity = "medium"
Expand All @@ -48,6 +49,7 @@ type AlertV2Interface interface {
AlertV2MetricInterface
AlertV2DowntimeInterface
AlertV2ChangeInterface
AlertV2FormBasedPrometheusInterface
}

type AlertV2PrometheusInterface interface {
Expand Down Expand Up @@ -82,6 +84,14 @@ type AlertV2ChangeInterface interface {
DeleteAlertV2Change(ctx context.Context, alertID int) error
}

type AlertV2FormBasedPrometheusInterface interface {
Base
CreateAlertV2FormBasedPrometheus(ctx context.Context, alert AlertV2FormBasedPrometheus) (AlertV2FormBasedPrometheus, error)
UpdateAlertV2FormBasedPrometheus(ctx context.Context, alert AlertV2FormBasedPrometheus) (AlertV2FormBasedPrometheus, error)
GetAlertV2FormBasedPrometheus(ctx context.Context, alertID int) (AlertV2FormBasedPrometheus, error)
DeleteAlertV2FormBasedPrometheus(ctx context.Context, alertID int) error
}

type AlertV2DowntimeInterface interface {
Base
CreateAlertV2Downtime(ctx context.Context, alert AlertV2Downtime) (AlertV2Downtime, error)
Expand Down Expand Up @@ -459,6 +469,82 @@ func (client *Client) DeleteAlertV2Change(ctx context.Context, alertID int) erro
return client.deleteAlertV2(ctx, alertID)
}

func (client *Client) CreateAlertV2FormBasedPrometheus(ctx context.Context, alert AlertV2FormBasedPrometheus) (AlertV2FormBasedPrometheus, error) {
err := client.addNotificationChannelType(ctx, alert.NotificationChannelConfigList)
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

err = client.translateScopeSegmentLabels(ctx, &alert.Config.ScopedSegmentedConfig)
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

payload, err := Marshal(alertV2FormBasedPrometheusWrapper{Alert: alert})
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

body, err := client.createAlertV2(ctx, payload)
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

wrapper, err := Unmarshal[alertV2FormBasedPrometheusWrapper](body)
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

return wrapper.Alert, nil
}

func (client *Client) UpdateAlertV2FormBasedPrometheus(ctx context.Context, alert AlertV2FormBasedPrometheus) (AlertV2FormBasedPrometheus, error) {
err := client.addNotificationChannelType(ctx, alert.NotificationChannelConfigList)
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

err = client.translateScopeSegmentLabels(ctx, &alert.Config.ScopedSegmentedConfig)
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

payload, err := Marshal(alertV2FormBasedPrometheusWrapper{Alert: alert})
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

body, err := client.updateAlertV2(ctx, alert.ID, payload)
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

wrapper, err := Unmarshal[alertV2FormBasedPrometheusWrapper](body)
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

return wrapper.Alert, nil
}

func (client *Client) GetAlertV2FormBasedPrometheus(ctx context.Context, alertID int) (AlertV2FormBasedPrometheus, error) {
body, err := client.getAlertV2(ctx, alertID)
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

wrapper, err := Unmarshal[alertV2FormBasedPrometheusWrapper](body)
if err != nil {
return AlertV2FormBasedPrometheus{}, err
}

return wrapper.Alert, nil
}

func (client *Client) DeleteAlertV2FormBasedPrometheus(ctx context.Context, alertID int) error {
return client.deleteAlertV2(ctx, alertID)
}

func (client *Client) createAlertV2(ctx context.Context, alertJson io.Reader) (io.ReadCloser, error) {
response, err := client.requester.Request(ctx, http.MethodPost, client.alertsV2URL(), alertJson)
if err != nil {
Expand Down
21 changes: 21 additions & 0 deletions sysdig/internal/client/v2/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,27 @@ type AlertV2ConfigChange struct {
LongerRangeSec int `json:"longerRangeSec"`
}

type AlertV2ConfigFormBasedPrometheus struct {
ScopedSegmentedConfig

Query string `json:"query"`
ConditionOperator string `json:"conditionOperator"`
Threshold float64 `json:"threshold"`
WarningConditionOperator string `json:"warningConditionOperator,omitempty"`
WarningThreshold *float64 `json:"warningThreshold,omitempty"`
NoDataBehaviour string `json:"noDataBehaviour"`
}

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"`
}

type alertV2FormBasedPrometheusWrapper struct {
Alert AlertV2FormBasedPrometheus `json:"alert"`
}

type AlertV2Change struct {
AlertV2Common
DurationSec int `json:"durationSec"` // not really used but the api wants it set to 0 in POST/PUT
Expand Down
1 change: 1 addition & 0 deletions sysdig/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func Provider() *schema.Provider {
"sysdig_monitor_alert_v2_downtime": resourceSysdigMonitorAlertV2Downtime(),
"sysdig_monitor_alert_v2_prometheus": resourceSysdigMonitorAlertV2Prometheus(),
"sysdig_monitor_alert_v2_change": resourceSysdigMonitorAlertV2Change(),
"sysdig_monitor_alert_v2_form_based_prometheus": resourceSysdigMonitorAlertV2FormBasedPrometheus(),
"sysdig_monitor_dashboard": resourceSysdigMonitorDashboard(),
"sysdig_monitor_notification_channel_email": resourceSysdigMonitorNotificationChannelEmail(),
"sysdig_monitor_notification_channel_opsgenie": resourceSysdigMonitorNotificationChannelOpsGenie(),
Expand Down
2 changes: 1 addition & 1 deletion sysdig/resource_sysdig_monitor_alert_v2_change_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ resource "sysdig_monitor_alert_v2_change" "sample" {
longer_time_range_seconds = 3600
link {
type = "runbook"
href = "http://ciao2.com"
href = "http://example.com"
}
link {
type = "dashboard"
Expand Down
229 changes: 229 additions & 0 deletions sysdig/resource_sysdig_monitor_alert_v2_form_based_prometheus.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package sysdig

import (
"context"
"fmt"
"strconv"
"time"

v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceSysdigMonitorAlertV2FormBasedPrometheus() *schema.Resource {

timeout := 5 * time.Minute

return &schema.Resource{
CreateContext: resourceSysdigMonitorAlertV2FormBasedPrometheusCreate,
UpdateContext: resourceSysdigMonitorAlertV2FormBasedPrometheusUpdate,
ReadContext: resourceSysdigMonitorAlertV2FormBasedPrometheusRead,
DeleteContext: resourceSysdigMonitorAlertV2FormBasedPrometheusDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(timeout),
Update: schema.DefaultTimeout(timeout),
Read: schema.DefaultTimeout(timeout),
Delete: schema.DefaultTimeout(timeout),
},

Schema: createScopedSegmentedAlertV2Schema(createAlertV2Schema(map[string]*schema.Schema{
"operator": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{">", ">=", "<", "<=", "=", "!="}, false),
},
"threshold": {
Type: schema.TypeFloat,
Required: true,
},
"warning_threshold": {
Type: schema.TypeString,
Optional: true,
Default: "",
},
"query": {
Type: schema.TypeString,
Required: true,
},
"no_data_behaviour": {
Type: schema.TypeString,
Optional: true,
Default: "DO_NOTHING",
ValidateFunc: validation.StringInSlice([]string{"DO_NOTHING", "TRIGGER"}, false),
},
})),
}
}

func getAlertV2FormBasedPrometheusClient(c SysdigClients) (v2.AlertV2FormBasedPrometheusInterface, error) {
return getAlertV2Client(c)
}

func resourceSysdigMonitorAlertV2FormBasedPrometheusCreate(ctx context.Context, d *schema.ResourceData, i interface{}) diag.Diagnostics {
client, err := getAlertV2FormBasedPrometheusClient(i.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}

a, err := buildAlertV2FormBasedPrometheusStruct(d)
if err != nil {
return diag.FromErr(err)
}

aCreated, err := client.CreateAlertV2FormBasedPrometheus(ctx, *a)
if err != nil {
return diag.FromErr(err)
}

d.SetId(strconv.Itoa(aCreated.ID))

err = updateAlertV2FormBasedPrometheusState(d, &aCreated)
if err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceSysdigMonitorAlertV2FormBasedPrometheusRead(ctx context.Context, d *schema.ResourceData, i interface{}) diag.Diagnostics {
client, err := getAlertV2FormBasedPrometheusClient(i.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}

id, err := strconv.Atoi(d.Id())
if err != nil {
return diag.FromErr(err)
}

a, err := client.GetAlertV2FormBasedPrometheus(ctx, id)
if err != nil {
if err == v2.AlertV2NotFound {
d.SetId("")
return nil
}
return diag.FromErr(err)
}

err = updateAlertV2FormBasedPrometheusState(d, &a)
if err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceSysdigMonitorAlertV2FormBasedPrometheusUpdate(ctx context.Context, d *schema.ResourceData, i interface{}) diag.Diagnostics {
client, err := getAlertV2FormBasedPrometheusClient(i.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}

a, err := buildAlertV2FormBasedPrometheusStruct(d)
if err != nil {
return diag.FromErr(err)
}

a.ID, _ = strconv.Atoi(d.Id())

aUpdated, err := client.UpdateAlertV2FormBasedPrometheus(ctx, *a)
if err != nil {
return diag.FromErr(err)
}

err = updateAlertV2FormBasedPrometheusState(d, &aUpdated)
if err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceSysdigMonitorAlertV2FormBasedPrometheusDelete(ctx context.Context, d *schema.ResourceData, i interface{}) diag.Diagnostics {
client, err := getAlertV2FormBasedPrometheusClient(i.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}

id, err := strconv.Atoi(d.Id())
if err != nil {
return diag.FromErr(err)
}

err = client.DeleteAlertV2FormBasedPrometheus(ctx, id)
if err != nil {
return diag.FromErr(err)
}

return nil
}

func buildAlertV2FormBasedPrometheusStruct(d *schema.ResourceData) (*v2.AlertV2FormBasedPrometheus, error) {
alertV2Common := buildAlertV2CommonStruct(d)
alertV2Common.Type = string(v2.AlertV2TypeFormBasedPrometheus)
config := v2.AlertV2ConfigFormBasedPrometheus{}

buildScopedSegmentedConfigStruct(d, &config.ScopedSegmentedConfig)

//ConditionOperator
config.ConditionOperator = d.Get("operator").(string)

//threshold
config.Threshold = d.Get("threshold").(float64)

//WarningThreshold
if warningThreshold, ok := d.GetOk("warning_threshold"); ok {
wts := warningThreshold.(string)
wt, err := strconv.ParseFloat(wts, 64)
if err != nil {
return nil, fmt.Errorf("cannot convert warning_threshold to a number: %w", err)
}
config.WarningThreshold = &wt
config.WarningConditionOperator = config.ConditionOperator
}

//Query
config.Query = d.Get("query").(string)

config.NoDataBehaviour = d.Get("no_data_behaviour").(string)

alert := &v2.AlertV2FormBasedPrometheus{
AlertV2Common: *alertV2Common,
DurationSec: 0,
Config: config,
}
return alert, nil
}

func updateAlertV2FormBasedPrometheusState(d *schema.ResourceData, alert *v2.AlertV2FormBasedPrometheus) error {
err := updateAlertV2CommonState(d, &alert.AlertV2Common)
if err != nil {
return err
}

err = updateScopedSegmentedConfigState(d, &alert.Config.ScopedSegmentedConfig)
if err != nil {
return err
}

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

_ = d.Set("threshold", alert.Config.Threshold)

if alert.Config.WarningThreshold != nil {
_ = d.Set("warning_threshold", fmt.Sprintf("%v", *alert.Config.WarningThreshold))
}

_ = d.Set("query", alert.Config.Query)

_ = d.Set("no_data_behaviour", alert.Config.NoDataBehaviour)

return nil
}
Loading

0 comments on commit f4938d2

Please sign in to comment.