Skip to content

Commit c3109e5

Browse files
authored
feat(cockpit): add support for alert manager (scaleway#2562)
* feat(cockpit): add ressource for alert manager need test * feat(cockpit): wait mep for continue * feat(cockpit): add ressource for alert manager and contact point * feat(cockpit): add ressource for alert manager and contact point * feat(cockpit): fix linter * feat(cockpit): merge alert manager and contact point in only one ressource * feat(cockpit): fix review * feat(cockpit): change emails to contact_points * feat(cockpit): add doc
1 parent 697c2fd commit c3109e5

12 files changed

+7777
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
subcategory: "Cockpit"
3+
page_title: "Scaleway: scaleway_cockpit_alert_manager"
4+
---
5+
6+
# Resource: scaleway_cockpit_alert_manager
7+
8+
Creates and manages Scaleway Cockpit Alert Managers.
9+
10+
For more information consult the [documentation](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#grafana-users).
11+
12+
## Example Usage
13+
14+
```terraform
15+
16+
resource "scaleway_account_project" "project" {
17+
name = "tf_test_project"
18+
}
19+
20+
resource "scaleway_cockpit_alert_manager" "alert_manager" {
21+
project_id = scaleway_account_project.project.id
22+
enable_managed_alerts = true
23+
contact_points = [
24+
{
25+
26+
},
27+
{
28+
29+
}
30+
]}
31+
```
32+
33+
34+
## Argument Reference
35+
36+
- `enable_managed_alerts` - (Optional, Boolean) Indicates whether the alert manager should be enabled. Defaults to true.
37+
- `contact_points` - (Optional, List of Map) A list of contact points with email addresses for the alert receivers. Each map should contain a single key email.
38+
- `project_id` - (Defaults to [provider](../index.md#project_id) `project_id`) The ID of the project the cockpit is associated with.
39+
- `region` - (Defaults to [provider](../index.md#arguments-reference) `region`) The [region](../guides/regions_and_zones.md#regions) in which alert_manager should be created.
40+
41+
## Attributes Reference
42+
43+
In addition to all arguments above, the following attributes are exported:
44+
45+
- `alert_manager_url` - Alert manager URL.
46+
47+
48+
## Import
49+
50+
Alert managers can be imported using the project ID, e.g.
51+
52+
```bash
53+
$ terraform import scaleway_cockpit_alert_manager.main fr-par/11111111-1111-1111-1111-111111111111
54+
```

internal/provider/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ func Provider(config *Config) plugin.ProviderFunc {
126126
"scaleway_cockpit_source": cockpit.ResourceCockpitSource(),
127127
"scaleway_cockpit_grafana_user": cockpit.ResourceCockpitGrafanaUser(),
128128
"scaleway_cockpit_token": cockpit.ResourceToken(),
129+
"scaleway_cockpit_alert_manager": cockpit.ResourceCockpitAlertManager(),
129130
"scaleway_container": container.ResourceContainer(),
130131
"scaleway_container_cron": container.ResourceCron(),
131132
"scaleway_container_domain": container.ResourceDomain(),
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
package cockpit
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
"github.com/scaleway/scaleway-sdk-go/api/cockpit/v1"
11+
"github.com/scaleway/scaleway-sdk-go/scw"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/verify"
15+
)
16+
17+
func ResourceCockpitAlertManager() *schema.Resource {
18+
return &schema.Resource{
19+
CreateContext: ResourceCockpitAlertManagerCreate,
20+
ReadContext: ResourceCockpitAlertManagerRead,
21+
UpdateContext: ResourceCockpitAlertManagerUpdate,
22+
DeleteContext: ResourceCockpitAlertManagerDelete,
23+
Importer: &schema.ResourceImporter{
24+
StateContext: schema.ImportStatePassthroughContext,
25+
},
26+
Schema: map[string]*schema.Schema{
27+
"project_id": account.ProjectIDSchema(),
28+
"enable_managed_alerts": {
29+
Type: schema.TypeBool,
30+
Optional: true,
31+
Default: true,
32+
Description: "Enable or disable the alert manager",
33+
},
34+
35+
"contact_points": {
36+
Type: schema.TypeList,
37+
Optional: true,
38+
Description: "A list of contact points",
39+
Elem: &schema.Resource{
40+
Schema: map[string]*schema.Schema{
41+
"email": {
42+
Type: schema.TypeString,
43+
Optional: true,
44+
ValidateFunc: verify.IsEmail(),
45+
Description: "Email addresses for the alert receivers",
46+
},
47+
},
48+
},
49+
},
50+
"region": regional.Schema(),
51+
"alert_manager_url": {
52+
Type: schema.TypeString,
53+
Computed: true,
54+
Description: "Alert manager URL",
55+
},
56+
},
57+
}
58+
}
59+
60+
func ResourceCockpitAlertManagerCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
61+
api, region, err := cockpitAPIWithRegion(d, meta)
62+
if err != nil {
63+
return diag.FromErr(err)
64+
}
65+
66+
projectID := d.Get("project_id").(string)
67+
contactPoints := d.Get("contact_points").([]interface{})
68+
EnableManagedAlerts := d.Get("enable_managed_alerts").(bool)
69+
70+
_, err = api.EnableAlertManager(&cockpit.RegionalAPIEnableAlertManagerRequest{
71+
Region: region,
72+
ProjectID: projectID,
73+
})
74+
if err != nil {
75+
return diag.FromErr(err)
76+
}
77+
if EnableManagedAlerts {
78+
_, err = api.EnableManagedAlerts(&cockpit.RegionalAPIEnableManagedAlertsRequest{
79+
Region: region,
80+
ProjectID: projectID,
81+
})
82+
if err != nil {
83+
return diag.FromErr(err)
84+
}
85+
}
86+
87+
if len(contactPoints) > 0 {
88+
for _, cp := range contactPoints {
89+
cpMap, ok := cp.(map[string]interface{})
90+
if !ok {
91+
return diag.FromErr(errors.New("invalid contact point format"))
92+
}
93+
94+
email, ok := cpMap["email"].(string)
95+
if !ok {
96+
return diag.FromErr(errors.New("invalid email format"))
97+
}
98+
99+
emailCP := &cockpit.ContactPointEmail{
100+
To: email,
101+
}
102+
103+
_, err = api.CreateContactPoint(&cockpit.RegionalAPICreateContactPointRequest{
104+
ProjectID: projectID,
105+
Email: emailCP,
106+
Region: region,
107+
}, scw.WithContext(ctx))
108+
if err != nil {
109+
return diag.FromErr(err)
110+
}
111+
}
112+
}
113+
114+
d.SetId(ResourceCockpitAlertManagerID(region, projectID))
115+
return ResourceCockpitAlertManagerRead(ctx, d, meta)
116+
}
117+
118+
func ResourceCockpitAlertManagerRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
119+
api, region, err := cockpitAPIWithRegion(d, meta)
120+
if err != nil {
121+
return diag.FromErr(err)
122+
}
123+
124+
projectID := d.Get("project_id").(string)
125+
126+
alertManager, err := api.GetAlertManager(&cockpit.RegionalAPIGetAlertManagerRequest{
127+
Region: region,
128+
ProjectID: projectID,
129+
}, scw.WithContext(ctx))
130+
if err != nil {
131+
return diag.FromErr(err)
132+
}
133+
134+
_ = d.Set("enable_managed_alerts", alertManager.ManagedAlertsEnabled)
135+
_ = d.Set("region", alertManager.Region)
136+
_ = d.Set("alert_manager_url", alertManager.AlertManagerURL)
137+
138+
contactPoints, err := api.ListContactPoints(&cockpit.RegionalAPIListContactPointsRequest{
139+
Region: region,
140+
ProjectID: projectID,
141+
}, scw.WithContext(ctx))
142+
if err != nil {
143+
return diag.FromErr(err)
144+
}
145+
146+
var contactPointsList []map[string]interface{}
147+
for _, cp := range contactPoints.ContactPoints {
148+
if cp.Email != nil {
149+
contactPoint := map[string]interface{}{
150+
"email": cp.Email.To,
151+
}
152+
contactPointsList = append(contactPointsList, contactPoint)
153+
}
154+
}
155+
_ = d.Set("contact_points", contactPointsList)
156+
return nil
157+
}
158+
159+
func ResourceCockpitAlertManagerUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
160+
api, region, err := cockpitAPIWithRegion(d, meta)
161+
if err != nil {
162+
return diag.FromErr(err)
163+
}
164+
projectID := d.Get("project_id").(string)
165+
166+
if d.HasChange("enable_managed_alerts") {
167+
enable := d.Get("enable_managed_alerts").(bool)
168+
if enable {
169+
_, err = api.EnableManagedAlerts(&cockpit.RegionalAPIEnableManagedAlertsRequest{
170+
Region: region,
171+
ProjectID: projectID,
172+
})
173+
} else {
174+
_, err = api.DisableManagedAlerts(&cockpit.RegionalAPIDisableManagedAlertsRequest{
175+
Region: region,
176+
ProjectID: projectID,
177+
}, scw.WithContext(ctx))
178+
}
179+
if err != nil {
180+
return diag.FromErr(err)
181+
}
182+
}
183+
if d.HasChange("contact_points") {
184+
oldContactPointsInterface, newContactPointsInterface := d.GetChange("contact_points")
185+
oldContactPoints := oldContactPointsInterface.([]interface{})
186+
newContactPoints := newContactPointsInterface.([]interface{})
187+
188+
oldContactMap := make(map[string]map[string]interface{})
189+
for _, oldCP := range oldContactPoints {
190+
cp := oldCP.(map[string]interface{})
191+
email := cp["email"].(string)
192+
oldContactMap[email] = cp
193+
}
194+
195+
newContactMap := make(map[string]map[string]interface{})
196+
for _, newCP := range newContactPoints {
197+
cp := newCP.(map[string]interface{})
198+
email := cp["email"].(string)
199+
newContactMap[email] = cp
200+
}
201+
for email := range oldContactMap {
202+
if _, found := newContactMap[email]; !found {
203+
err := api.DeleteContactPoint(&cockpit.RegionalAPIDeleteContactPointRequest{
204+
Region: region,
205+
ProjectID: projectID,
206+
Email: &cockpit.ContactPointEmail{To: email},
207+
}, scw.WithContext(ctx))
208+
if err != nil {
209+
return diag.FromErr(err)
210+
}
211+
}
212+
}
213+
214+
for email := range newContactMap {
215+
if _, found := oldContactMap[email]; !found {
216+
contactPointEmail := &cockpit.ContactPointEmail{To: email}
217+
_, err = api.CreateContactPoint(&cockpit.RegionalAPICreateContactPointRequest{
218+
Region: region,
219+
ProjectID: projectID,
220+
Email: contactPointEmail,
221+
}, scw.WithContext(ctx))
222+
if err != nil {
223+
return diag.FromErr(err)
224+
}
225+
}
226+
}
227+
}
228+
229+
return ResourceCockpitAlertManagerRead(ctx, d, meta)
230+
}
231+
232+
func ResourceCockpitAlertManagerDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
233+
api, region, err := cockpitAPIWithRegion(d, meta)
234+
if err != nil {
235+
return diag.FromErr(err)
236+
}
237+
238+
projectID := d.Get("project_id").(string)
239+
240+
contactPoints, err := api.ListContactPoints(&cockpit.RegionalAPIListContactPointsRequest{
241+
Region: region,
242+
ProjectID: projectID,
243+
}, scw.WithContext(ctx))
244+
if err != nil {
245+
return diag.FromErr(err)
246+
}
247+
248+
for _, cp := range contactPoints.ContactPoints {
249+
if cp.Email != nil {
250+
err = api.DeleteContactPoint(&cockpit.RegionalAPIDeleteContactPointRequest{
251+
Region: region,
252+
ProjectID: projectID,
253+
Email: &cockpit.ContactPointEmail{To: cp.Email.To},
254+
}, scw.WithContext(ctx))
255+
if err != nil {
256+
return diag.FromErr(err)
257+
}
258+
}
259+
}
260+
261+
_, err = api.DisableManagedAlerts(&cockpit.RegionalAPIDisableManagedAlertsRequest{
262+
Region: region,
263+
ProjectID: projectID,
264+
}, scw.WithContext(ctx))
265+
if err != nil {
266+
return diag.FromErr(err)
267+
}
268+
_, err = api.DisableAlertManager(&cockpit.RegionalAPIDisableAlertManagerRequest{
269+
Region: region,
270+
ProjectID: projectID,
271+
})
272+
if err != nil {
273+
return diag.FromErr(err)
274+
}
275+
276+
d.SetId("")
277+
278+
return nil
279+
}
280+
281+
func ResourceCockpitAlertManagerID(region scw.Region, projectID string) (resourceID string) {
282+
return fmt.Sprintf("%s/%s/1", region, projectID)
283+
}

0 commit comments

Comments
 (0)