Skip to content

Commit 02c7b6b

Browse files
authored
feat(monitor): resource for silence rule (#390)
1 parent b20e721 commit 02c7b6b

File tree

8 files changed

+558
-0
lines changed

8 files changed

+558
-0
lines changed

sysdig/internal/client/v2/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type MonitorCommon interface {
5151
AlertInterface
5252
AlertV2Interface
5353
DashboardInterface
54+
SilenceRuleInterface
5455
}
5556

5657
type SecureCommon interface {

sysdig/internal/client/v2/model.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,3 +714,16 @@ type IdentityContext struct {
714714
ServiceAccountID int `json:"serviceAccountId"`
715715
ServiceAccountName string `json:"serviceAccountName"`
716716
}
717+
718+
type SilenceRule struct {
719+
Name string `json:"name"`
720+
Enabled bool `json:"enabled"`
721+
StartTs int64 `json:"startTs"`
722+
DurationInSec int `json:"durationInSec"`
723+
Scope string `json:"scope,omitempty"`
724+
AlertIds []int `json:"alertIds,omitempty"`
725+
NotificationChannelIds []int `json:"notificationChannelIds,omitempty"`
726+
727+
Version int `json:"version,omitempty"`
728+
ID int `json:"id,omitempty"`
729+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package v2
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"net/http"
8+
)
9+
10+
const (
11+
silenceRulesPath = "%s/api/v1/silencingRules"
12+
silenceRulePath = "%s/api/v1/silencingRules/%d"
13+
)
14+
15+
var SilenceRuleNotFound = errors.New("silence rule not found")
16+
17+
type SilenceRuleInterface interface {
18+
Base
19+
GetSilenceRule(ctx context.Context, id int) (SilenceRule, error)
20+
CreateSilenceRule(ctx context.Context, silenceRule SilenceRule) (SilenceRule, error)
21+
UpdateSilenceRule(ctx context.Context, silenceRule SilenceRule) (SilenceRule, error)
22+
DeleteSilenceRule(ctx context.Context, id int) error
23+
}
24+
25+
func (client *Client) GetSilenceRule(ctx context.Context, id int) (SilenceRule, error) {
26+
response, err := client.requester.Request(ctx, http.MethodGet, client.getSilenceRuleURL(id), nil)
27+
if err != nil {
28+
return SilenceRule{}, err
29+
}
30+
defer response.Body.Close()
31+
32+
if response.StatusCode == http.StatusNotFound {
33+
return SilenceRule{}, SilenceRuleNotFound
34+
}
35+
if response.StatusCode != http.StatusOK {
36+
return SilenceRule{}, client.ErrorFromResponse(response)
37+
}
38+
39+
silenceRule, err := Unmarshal[SilenceRule](response.Body)
40+
if err != nil {
41+
return SilenceRule{}, err
42+
}
43+
44+
return silenceRule, nil
45+
}
46+
47+
func (client *Client) CreateSilenceRule(ctx context.Context, silenceRule SilenceRule) (SilenceRule, error) {
48+
payload, err := Marshal(silenceRule)
49+
if err != nil {
50+
return SilenceRule{}, err
51+
}
52+
53+
response, err := client.requester.Request(ctx, http.MethodPost, client.getSilenceRulesURL(), payload)
54+
if err != nil {
55+
return SilenceRule{}, err
56+
}
57+
defer response.Body.Close()
58+
59+
if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusCreated {
60+
return SilenceRule{}, client.ErrorFromResponse(response)
61+
}
62+
63+
return Unmarshal[SilenceRule](response.Body)
64+
}
65+
66+
func (client *Client) UpdateSilenceRule(ctx context.Context, silenceRule SilenceRule) (SilenceRule, error) {
67+
payload, err := Marshal(silenceRule)
68+
if err != nil {
69+
return SilenceRule{}, err
70+
}
71+
72+
response, err := client.requester.Request(ctx, http.MethodPut, client.getSilenceRuleURL(silenceRule.ID), payload)
73+
if err != nil {
74+
return SilenceRule{}, err
75+
}
76+
defer response.Body.Close()
77+
78+
if response.StatusCode != http.StatusOK {
79+
return SilenceRule{}, client.ErrorFromResponse(response)
80+
}
81+
82+
return Unmarshal[SilenceRule](response.Body)
83+
}
84+
85+
func (client *Client) DeleteSilenceRule(ctx context.Context, id int) error {
86+
response, err := client.requester.Request(ctx, http.MethodDelete, client.getSilenceRuleURL(id), nil)
87+
if err != nil {
88+
return err
89+
}
90+
defer response.Body.Close()
91+
92+
if response.StatusCode != http.StatusNoContent && response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNotFound {
93+
return client.ErrorFromResponse(response)
94+
}
95+
96+
return nil
97+
}
98+
99+
func (client *Client) getSilenceRulesURL() string {
100+
return fmt.Sprintf(silenceRulesPath, client.config.url)
101+
}
102+
103+
func (client *Client) getSilenceRuleURL(id int) string {
104+
return fmt.Sprintf(silenceRulePath, client.config.url, id)
105+
}

sysdig/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ func Provider() *schema.Provider {
134134
"sysdig_secure_scanning_policy": resourceSysdigSecureScanningPolicy(),
135135
"sysdig_secure_scanning_policy_assignment": resourceSysdigSecureScanningPolicyAssignment(),
136136

137+
"sysdig_monitor_silence_rule": resourceSysdigMonitorSilenceRule(),
137138
"sysdig_monitor_alert_downtime": resourceSysdigMonitorAlertDowntime(),
138139
"sysdig_monitor_alert_metric": resourceSysdigMonitorAlertMetric(),
139140
"sysdig_monitor_alert_event": resourceSysdigMonitorAlertEvent(),
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package sysdig
2+
3+
import (
4+
"context"
5+
"strconv"
6+
"time"
7+
8+
v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2"
9+
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
13+
)
14+
15+
func resourceSysdigMonitorSilenceRule() *schema.Resource {
16+
timeout := 5 * time.Minute
17+
18+
return &schema.Resource{
19+
CreateContext: resourceSysdigMonitorSilenceRuleCreate,
20+
UpdateContext: resourceSysdigMonitorSilenceRuleUpdate,
21+
ReadContext: resourceSysdigMonitorSilenceRuleRead,
22+
DeleteContext: resourceSysdigMonitorSilenceRuleDelete,
23+
Importer: &schema.ResourceImporter{
24+
StateContext: schema.ImportStatePassthroughContext,
25+
},
26+
27+
Timeouts: &schema.ResourceTimeout{
28+
Create: schema.DefaultTimeout(timeout),
29+
Update: schema.DefaultTimeout(timeout),
30+
Read: schema.DefaultTimeout(timeout),
31+
Delete: schema.DefaultTimeout(timeout),
32+
},
33+
34+
Schema: map[string]*schema.Schema{
35+
"name": {
36+
Type: schema.TypeString,
37+
Required: true,
38+
},
39+
"enabled": {
40+
Type: schema.TypeBool,
41+
Optional: true,
42+
Default: true,
43+
},
44+
"start_ts": {
45+
Type: schema.TypeString,
46+
Required: true,
47+
},
48+
"duration_seconds": {
49+
Type: schema.TypeInt,
50+
Required: true,
51+
ValidateFunc: validation.IntAtLeast(60),
52+
},
53+
"alert_ids": {
54+
Type: schema.TypeSet,
55+
Elem: &schema.Schema{
56+
Type: schema.TypeInt,
57+
},
58+
Optional: true,
59+
},
60+
"scope": {
61+
Type: schema.TypeString,
62+
Optional: true,
63+
},
64+
"notification_channel_ids": {
65+
Type: schema.TypeSet,
66+
Elem: &schema.Schema{
67+
Type: schema.TypeInt,
68+
},
69+
Optional: true,
70+
},
71+
"version": {
72+
Type: schema.TypeInt,
73+
Computed: true,
74+
},
75+
},
76+
}
77+
}
78+
79+
func getMonitorSilenceRuleClient(c SysdigClients) (v2.SilenceRuleInterface, error) {
80+
var client v2.SilenceRuleInterface
81+
var err error
82+
switch c.GetClientType() {
83+
case IBMMonitor:
84+
client, err = c.ibmMonitorClient()
85+
if err != nil {
86+
return nil, err
87+
}
88+
default:
89+
client, err = c.sysdigMonitorClientV2()
90+
if err != nil {
91+
return nil, err
92+
}
93+
}
94+
return client, nil
95+
}
96+
97+
func resourceSysdigMonitorSilenceRuleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
98+
client, err := getMonitorSilenceRuleClient(meta.(SysdigClients))
99+
if err != nil {
100+
return diag.FromErr(err)
101+
}
102+
103+
silenceRule, err := monitorSilenceRuleFromResourceData(d)
104+
if err != nil {
105+
return diag.FromErr(err)
106+
}
107+
108+
silenceRule, err = client.CreateSilenceRule(ctx, silenceRule)
109+
if err != nil {
110+
return diag.FromErr(err)
111+
}
112+
113+
d.SetId(strconv.Itoa(silenceRule.ID))
114+
115+
return resourceSysdigMonitorSilenceRuleRead(ctx, d, meta)
116+
}
117+
118+
func resourceSysdigMonitorSilenceRuleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
119+
client, err := getMonitorSilenceRuleClient(meta.(SysdigClients))
120+
if err != nil {
121+
return diag.FromErr(err)
122+
}
123+
124+
id, err := strconv.Atoi(d.Id())
125+
if err != nil {
126+
return diag.FromErr(err)
127+
}
128+
129+
silenceRule, err := client.GetSilenceRule(ctx, id)
130+
131+
if err != nil {
132+
if err == v2.SilenceRuleNotFound {
133+
d.SetId("")
134+
return nil
135+
}
136+
return diag.FromErr(err)
137+
}
138+
139+
// suppress diff of "enabled" field if the silence interval is over: it will always be false from the api
140+
// any update of an ended silence rule results in a 422 Unprocessable Entity error from the api
141+
silenceRuleEnd := time.Unix(silenceRule.StartTs/1000+int64(silenceRule.DurationInSec), 0)
142+
if time.Now().After(silenceRuleEnd) {
143+
silenceRule.Enabled = d.Get("enabled").(bool)
144+
}
145+
146+
err = monitorSilenceRuleToResourceData(silenceRule, d)
147+
if err != nil {
148+
return diag.FromErr(err)
149+
}
150+
151+
return nil
152+
}
153+
154+
func resourceSysdigMonitorSilenceRuleUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
155+
client, err := getMonitorSilenceRuleClient(meta.(SysdigClients))
156+
if err != nil {
157+
return diag.FromErr(err)
158+
}
159+
160+
silenceRule, err := monitorSilenceRuleFromResourceData(d)
161+
if err != nil {
162+
return diag.FromErr(err)
163+
}
164+
165+
silenceRule.Version = d.Get("version").(int)
166+
silenceRule.ID, err = strconv.Atoi(d.Id())
167+
if err != nil {
168+
return diag.FromErr(err)
169+
}
170+
171+
_, err = client.UpdateSilenceRule(ctx, silenceRule)
172+
if err != nil {
173+
return diag.FromErr(err)
174+
}
175+
176+
return nil
177+
}
178+
179+
func resourceSysdigMonitorSilenceRuleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
180+
client, err := getMonitorSilenceRuleClient(meta.(SysdigClients))
181+
if err != nil {
182+
return diag.FromErr(err)
183+
}
184+
185+
id, err := strconv.Atoi(d.Id())
186+
if err != nil {
187+
return diag.FromErr(err)
188+
}
189+
190+
err = client.DeleteSilenceRule(ctx, id)
191+
if err != nil {
192+
return diag.FromErr(err)
193+
}
194+
195+
return nil
196+
}
197+
198+
func monitorSilenceRuleFromResourceData(d *schema.ResourceData) (v2.SilenceRule, error) {
199+
silenceRule := v2.SilenceRule{}
200+
201+
silenceRule.Name = d.Get("name").(string)
202+
silenceRule.Enabled = d.Get("enabled").(bool)
203+
startTs, err := strconv.ParseInt(d.Get("start_ts").(string), 10, 64)
204+
if err != nil {
205+
return silenceRule, err
206+
}
207+
silenceRule.StartTs = startTs
208+
silenceRule.DurationInSec = d.Get("duration_seconds").(int)
209+
silenceRule.Scope = d.Get("scope").(string)
210+
alertIds := d.Get("alert_ids").(*schema.Set)
211+
for _, rawAlertId := range alertIds.List() {
212+
if alertId, ok := rawAlertId.(int); ok {
213+
silenceRule.AlertIds = append(silenceRule.AlertIds, alertId)
214+
}
215+
}
216+
notificationChannelIds := d.Get("notification_channel_ids").(*schema.Set)
217+
for _, rawNotificationChannelId := range notificationChannelIds.List() {
218+
if notificationChannelId, ok := rawNotificationChannelId.(int); ok {
219+
silenceRule.NotificationChannelIds = append(silenceRule.NotificationChannelIds, notificationChannelId)
220+
}
221+
}
222+
return silenceRule, nil
223+
}
224+
225+
func monitorSilenceRuleToResourceData(silenceRule v2.SilenceRule, d *schema.ResourceData) (err error) {
226+
_ = d.Set("name", silenceRule.Name)
227+
_ = d.Set("enabled", silenceRule.Enabled)
228+
_ = d.Set("start_ts", strconv.FormatInt(silenceRule.StartTs, 10))
229+
_ = d.Set("duration_seconds", silenceRule.DurationInSec)
230+
_ = d.Set("alert_ids", silenceRule.AlertIds)
231+
_ = d.Set("scope", silenceRule.Scope)
232+
_ = d.Set("notification_channel_ids", silenceRule.NotificationChannelIds)
233+
_ = d.Set("version", silenceRule.Version)
234+
return nil
235+
}

0 commit comments

Comments
 (0)