Skip to content

Commit f1e98dc

Browse files
committed
feat: Add resources for Monitor alerts
Signed-off-by: Federico Barcelona <[email protected]>
1 parent 0fd4ce6 commit f1e98dc

25 files changed

+1567
-38
lines changed

examples/alert.tf

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
2+
resource "sysdig_monitor_alert_anomaly" "sample" {
3+
name = "[Kubernetes] Anomaly Detection Alert"
4+
description = "Detects an anomaly in the cluster"
5+
severity = 6
6+
7+
monitor = ["cpu.used.percent", "memory.bytes.used"]
8+
9+
multiple_alerts_by = ["kubernetes.cluster.name",
10+
"kubernetes.namespace.name",
11+
"kubernetes.deployment.name",
12+
"kubernetes.pod.name"]
13+
}
14+
15+
resource "sysdig_monitor_alert_downtime" "sample" {
16+
name = "[Kubernetes] Downtime Alert"
17+
description = "Detects a downtime in the Kubernetes cluster"
18+
severity = 2
19+
20+
entities_to_monitor = ["kubernetes.namespace.name"]
21+
22+
trigger_after_minutes = 10
23+
trigger_after_pct = 100
24+
}
25+
26+
resource "sysdig_monitor_alert_event" "sample" {
27+
name = "[Kubernetes] Failed to pull image"
28+
description = "A Kubernetes pod failed to pull an image from the registry"
29+
severity = 4
30+
31+
event_name = "Failed to pull image"
32+
source = "kubernetes"
33+
event_rel = ">"
34+
event_count = 0
35+
36+
multiple_alerts_by = ["kubernetes.pod.name"]
37+
38+
trigger_after_minutes = 1
39+
}
40+
41+
resource "sysdig_monitor_alert_group_outlier" "sample" {
42+
name = "[Kubernetes] A node is using more CPU than the rest"
43+
description = "Monitors the cluster and checks when a node has more CPU usage than the others"
44+
severity = 6
45+
46+
monitor = ["cpu.used.percent"]
47+
48+
trigger_after_minutes = 10
49+
50+
capture {
51+
filename = "TERRAFORM_TEST"
52+
duration = 15
53+
}
54+
}
55+
56+
resource "sysdig_monitor_alert_metric" "sample" {
57+
name = "[Kubernetes] CrashLoopBackOff"
58+
description = "A Kubernetes pod failed to restart"
59+
severity = 6
60+
61+
metric = "sum(timeAvg(kubernetes.pod.restart.count)) > 2"
62+
trigger_after_minutes = 1
63+
64+
multiple_alerts_by = ["kubernetes.cluster.name",
65+
"kubernetes.namespace.name",
66+
"kubernetes.deployment.name",
67+
"kubernetes.pod.name"]
68+
69+
capture {
70+
filename = "CrashLoopBackOff"
71+
duration = 15
72+
}
73+
}
74+

sysdig/monitor/alerts.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package monitor
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io/ioutil"
7+
"net/http"
8+
)
9+
10+
func (c *sysdigMonitorClient) CreateAlert(alert Alert) (createdAlert Alert, err error) {
11+
//data, err := ioutil.ReadAll(alert.ToJSON())
12+
//if err != nil {
13+
// return
14+
//}
15+
//err = errors.New(string(data))
16+
//return
17+
18+
response, err := c.doSysdigMonitorRequest(http.MethodPost, c.alertsURL(), alert.ToJSON())
19+
if err != nil {
20+
return
21+
}
22+
body, err := ioutil.ReadAll(response.Body)
23+
if err != nil {
24+
return
25+
}
26+
27+
if response.StatusCode != 200 {
28+
err = errors.New(string(body))
29+
return
30+
}
31+
32+
defer response.Body.Close()
33+
34+
return AlertFromJSON(body), nil
35+
}
36+
37+
func (c *sysdigMonitorClient) DeleteAlert(alertID int) error {
38+
response, err := c.doSysdigMonitorRequest(http.MethodDelete, c.alertURL(alertID), nil)
39+
40+
defer response.Body.Close()
41+
42+
return err
43+
}
44+
45+
func (c *sysdigMonitorClient) UpdateAlert(alert Alert) (updatedAlert Alert, err error) {
46+
response, err := c.doSysdigMonitorRequest(http.MethodPut, c.alertURL(alert.ID), alert.ToJSON())
47+
if err != nil {
48+
return
49+
}
50+
51+
body, err := ioutil.ReadAll(response.Body)
52+
if err != nil {
53+
return
54+
}
55+
56+
if response.StatusCode != 200 {
57+
err = errors.New(string(body))
58+
return
59+
}
60+
61+
defer response.Body.Close()
62+
63+
return AlertFromJSON(body), nil
64+
}
65+
66+
func (c *sysdigMonitorClient) GetAlertById(alertID int) (alert Alert, err error) {
67+
response, err := c.doSysdigMonitorRequest(http.MethodGet, c.alertURL(alertID), nil)
68+
if err != nil {
69+
return
70+
}
71+
body, err := ioutil.ReadAll(response.Body)
72+
if err != nil {
73+
return
74+
}
75+
76+
if response.StatusCode != 200 {
77+
err = errors.New(string(body))
78+
return
79+
}
80+
81+
defer response.Body.Close()
82+
83+
return AlertFromJSON(body), nil
84+
}
85+
86+
func (c *sysdigMonitorClient) alertsURL() string {
87+
return fmt.Sprintf("%s/api/alerts", c.URL)
88+
}
89+
90+
func (c *sysdigMonitorClient) alertURL(alertID int) string {
91+
return fmt.Sprintf("%s/api/alerts/%d", c.URL, alertID)
92+
93+
}

sysdig/monitor/client.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package monitor
2+
3+
import (
4+
"io"
5+
"net/http"
6+
)
7+
8+
type SysdigMonitorClient interface {
9+
CreateAlert(Alert) (Alert, error)
10+
DeleteAlert(int) error
11+
UpdateAlert(Alert) (Alert, error)
12+
GetAlertById(int) (Alert, error)
13+
}
14+
15+
func NewSysdigMonitorClient(apiToken string, url string) SysdigMonitorClient {
16+
return &sysdigMonitorClient{
17+
SysdigMonitorAPIToken: apiToken,
18+
URL: url,
19+
httpClient: http.DefaultClient,
20+
}
21+
}
22+
23+
type sysdigMonitorClient struct {
24+
SysdigMonitorAPIToken string
25+
URL string
26+
httpClient *http.Client
27+
}
28+
29+
func (c *sysdigMonitorClient) doSysdigMonitorRequest(method string, url string, payload io.Reader) (*http.Response, error) {
30+
request, _ := http.NewRequest(method, url, payload)
31+
request.Header.Set("Authorization", "Bearer "+c.SysdigMonitorAPIToken)
32+
request.Header.Set("Content-Type", "application/json")
33+
34+
return c.httpClient.Do(request)
35+
}

sysdig/monitor/models.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package monitor
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"io"
7+
)
8+
9+
type CustomNotification struct {
10+
TitleTemplate string `json:"titleTemplate"`
11+
UseNewTemplate bool `json:"useNewTemplate"`
12+
}
13+
14+
type SysdigCapture struct {
15+
Name string `json:"name"`
16+
Filters string `json:"filters,omitempty"`
17+
Duration int `json:"duration"`
18+
Type string `json:"type,omitempty"`
19+
BucketName string `json:"bucketName"`
20+
Folder string `json:"folder,omitempty"`
21+
Enabled bool `json:"enabled"`
22+
StorageID interface{} `json:"storageId,omitempty"`
23+
}
24+
type SegmentCondition struct {
25+
Type string `json:"type"`
26+
}
27+
28+
type Criteria struct {
29+
Text string `json:"text"`
30+
Source string `json:"source"`
31+
}
32+
33+
type Monitor struct {
34+
Metric string `json:"metric"`
35+
StdDevFactor float64 `json:"stdDevFactor"`
36+
}
37+
38+
type alertWrapper struct {
39+
Alert Alert `json:"alert"`
40+
}
41+
42+
type Alert struct {
43+
ID int `json:"id,omitempty"`
44+
Version int `json:"version,omitempty"`
45+
Type string `json:"type"` // computed MANUAL
46+
Name string `json:"name"`
47+
Description string `json:"description"`
48+
Enabled bool `json:"enabled"`
49+
NotificationChannelIds []int `json:"notificationChannelIds"`
50+
Filter string `json:"filter"`
51+
Severity int `json:"severity"` // 6 == INFO, 4 == LOW, 2 == MEDIUM, 0 == HIGH // NOT USED
52+
Timespan int `json:"timespan"` // computed 600000000
53+
CustomNotification *CustomNotification `json:"customNotification"`
54+
TeamID int `json:"teamId,omitempty"` // computed
55+
AutoCreated bool `json:"autoCreated"`
56+
SysdigCapture *SysdigCapture `json:"sysdigCapture"`
57+
RateOfChange bool `json:"rateOfChange,omitempty"`
58+
ReNotifyMinutes int `json:"reNotifyMinutes"`
59+
ReNotify bool `json:"reNotify"`
60+
Valid bool `json:"valid"`
61+
SeverityLabel string `json:"severityLabel,omitempty"` // MEDIUM == MEDIUM, LOW == LOW, NONE == INFO, HIGH == HIGH
62+
SegmentBy []string `json:"segmentBy"`
63+
SegmentCondition *SegmentCondition `json:"segmentCondition"`
64+
Criteria *Criteria `json:"criteria,omitempty"`
65+
Monitor []*Monitor `json:"monitor,omitempty"`
66+
Condition string `json:"condition"`
67+
SeverityLevel int `json:"severityLevel,omitempty"` // 0 == MEDIUM, 2 == LOW, 4 == INFO, 6 == HIGH
68+
}
69+
70+
func (a *Alert) ToJSON() io.Reader {
71+
payload, _ := json.Marshal(alertWrapper{Alert: *a})
72+
return bytes.NewBuffer(payload)
73+
}
74+
75+
func AlertFromJSON(body []byte) Alert {
76+
var result alertWrapper
77+
json.Unmarshal(body, &result)
78+
79+
return result.Alert
80+
}

sysdig/provider.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
55
"github.com/hashicorp/terraform-plugin-sdk/terraform"
66

7+
"github.com/draios/terraform-provider-sysdig/sysdig/monitor"
78
"github.com/draios/terraform-provider-sysdig/sysdig/secure"
89
)
910

@@ -21,6 +22,17 @@ func Provider() terraform.ResourceProvider {
2122
Required: true,
2223
DefaultFunc: schema.EnvDefaultFunc("SYSDIG_SECURE_URL", "https://secure.sysdig.com"),
2324
},
25+
"sysdig_monitor_api_token": {
26+
Type: schema.TypeString,
27+
Required: true,
28+
Sensitive: true,
29+
DefaultFunc: schema.EnvDefaultFunc("SYSDIG_MONITOR_API_TOKEN", nil),
30+
},
31+
"sysdig_monitor_url": {
32+
Type: schema.TypeString,
33+
Required: true,
34+
DefaultFunc: schema.EnvDefaultFunc("SYSDIG_MONITOR_URL", "https://app.sysdigcloud.com"),
35+
},
2436
},
2537
ResourcesMap: map[string]*schema.Resource{
2638
"sysdig_secure_policy": resourceSysdigSecurePolicy(),
@@ -31,15 +43,32 @@ func Provider() terraform.ResourceProvider {
3143
"sysdig_secure_rule_process": resourceSysdigSecureRuleProcess(),
3244
"sysdig_secure_rule_syscall": resourceSysdigSecureRuleSyscall(),
3345
"sysdig_secure_rule_falco": resourceSysdigSecureRuleFalco(),
46+
47+
"sysdig_monitor_alert_downtime": resourceSysdigMonitorAlertDowntime(),
48+
"sysdig_monitor_alert_metric": resourceSysdigMonitorAlertMetric(),
49+
"sysdig_monitor_alert_event": resourceSysdigMonitorAlertEvent(),
50+
"sysdig_monitor_alert_anomaly": resourceSysdigMonitorAlertAnomaly(),
51+
"sysdig_monitor_alert_group_outlier": resourceSysdigMonitorAlertGroupOutlier(),
3452
},
3553
ConfigureFunc: providerConfigure,
3654
}
3755
}
3856

57+
type SysdigClients struct {
58+
sysdigMonitorClient monitor.SysdigMonitorClient
59+
sysdigSecureClient secure.SysdigSecureClient
60+
}
61+
3962
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
40-
sysdigSecureClient := secure.NewSysdigSecureClient(
41-
d.Get("sysdig_secure_api_token").(string),
42-
d.Get("sysdig_secure_url").(string),
43-
)
44-
return sysdigSecureClient, nil
63+
sysdigClient := &SysdigClients{
64+
sysdigMonitorClient: monitor.NewSysdigMonitorClient(
65+
d.Get("sysdig_monitor_api_token").(string),
66+
d.Get("sysdig_monitor_url").(string),
67+
),
68+
sysdigSecureClient: secure.NewSysdigSecureClient(
69+
d.Get("sysdig_secure_api_token").(string),
70+
d.Get("sysdig_secure_url").(string),
71+
),
72+
}
73+
return sysdigClient, nil
4574
}

0 commit comments

Comments
 (0)