diff --git a/docs/data-sources/cockpit_preconfigured_alert.md b/docs/data-sources/cockpit_preconfigured_alert.md new file mode 100644 index 000000000..c3f84efa9 --- /dev/null +++ b/docs/data-sources/cockpit_preconfigured_alert.md @@ -0,0 +1,100 @@ +--- +subcategory: "Cockpit" +page_title: "Scaleway: scaleway_cockpit_preconfigured_alert" +--- + +# Data Source: scaleway_cockpit_preconfigured_alert + +Gets information about preconfigured alert rules available in Scaleway Cockpit. + +Preconfigured alerts are ready-to-use alert rules that monitor common metrics for Scaleway services. +You can enable these alerts in your Alert Manager using the `scaleway_cockpit_alert_manager` resource. + +For more information, refer to Cockpit's [product documentation](https://www.scaleway.com/en/docs/observability/cockpit/concepts/) and [API documentation](https://www.scaleway.com/en/developers/api/cockpit/regional-api). + +## Example Usage + +### Basic usage + +```terraform +data "scaleway_cockpit_preconfigured_alert" "main" { + project_id = scaleway_account_project.project.id +} + +output "available_alerts" { + value = data.scaleway_cockpit_preconfigured_alert.main.alerts +} +``` + +### Filter by status + +```terraform +data "scaleway_cockpit_preconfigured_alert" "enabled" { + project_id = scaleway_account_project.project.id + rule_status = "enabled" +} + +data "scaleway_cockpit_preconfigured_alert" "disabled" { + project_id = scaleway_account_project.project.id + rule_status = "disabled" +} +``` + +### Use with Alert Manager + +```terraform +resource "scaleway_account_project" "project" { + name = "my-observability-project" +} + +resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id +} + +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_cockpit.main.project_id +} + +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_cockpit.main.project_id + + # Enable specific alerts by their preconfigured_rule_id + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "instance" && alert.rule_status == "disabled" + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +## Argument Reference + +- `project_id` - (Optional) The ID of the project the alerts are associated with. If not provided, the default project configured in the provider is used. +- `region` - (Optional, defaults to provider region) The region in which the alerts exist. +- `data_source_id` - (Optional) Filter alerts by data source ID. +- `rule_status` - (Optional) Filter alerts by rule status. Valid values are `enabled` or `disabled`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `id` - The ID of the resource (project ID with region). +- `alerts` - List of preconfigured alerts. Each alert contains: + - `name` - Name of the alert rule. + - `rule` - PromQL expression defining the alert condition. + - `duration` - Duration for which the condition must be true before the alert fires (e.g., "5m"). + - `rule_status` - Status of the alert rule (`enabled`, `disabled`, `enabling`, `disabling`). + - `state` - Current state of the alert (`inactive`, `pending`, `firing`). + - `annotations` - Map of annotations attached to the alert. + - `preconfigured_rule_id` - Unique identifier of the preconfigured rule. Use this ID in `scaleway_cockpit_alert_manager` resource. + - `display_name` - Human-readable name of the alert. + - `display_description` - Human-readable description of the alert. + - `product_name` - Scaleway product associated with the alert (e.g., "instance", "rdb", "kubernetes"). + - `product_family` - Family of the product (e.g., "compute", "storage", "network"). + - `data_source_id` - ID of the data source containing the alert rule. + + diff --git a/docs/guides/migration_guide_cockpit_alert_manager.md b/docs/guides/migration_guide_cockpit_alert_manager.md new file mode 100644 index 000000000..396fe44c6 --- /dev/null +++ b/docs/guides/migration_guide_cockpit_alert_manager.md @@ -0,0 +1,166 @@ +--- +page_title: "Cockpit Alert Manager Migration Guide" +--- + +# Cockpit Alert Manager Migration Guide + +This guide explains how to migrate from the deprecated `enable_managed_alerts` field to the new `preconfigured_alert_ids` field in the `scaleway_cockpit_alert_manager` resource. + +## Background + +The `enable_managed_alerts` field is being deprecated in favor of a more flexible approach using `preconfigured_alert_ids`. This change provides: + +- **Granular control**: Select specific alerts instead of enabling all managed alerts +- **Better visibility**: Explicitly declare which alerts are enabled in your Terraform configuration +- **Improved state management**: Terraform accurately tracks which alerts are active + +## Migration Steps + +### Before Migration (Deprecated) + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + enable_managed_alerts = true + + contact_points { + email = "alerts@example.com" + } +} +``` + +### After Migration (Recommended) + +#### Step 1: List Available Preconfigured Alerts + +Use the data source to discover available alerts: + +```terraform +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_account_project.project.id +} + +output "available_alerts" { + value = data.scaleway_cockpit_preconfigured_alert.all.alerts +} +``` + +Run `terraform apply` and review the output to see available alerts. + +#### Step 2: Select Specific Alerts + +Choose the alerts you want to enable: + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Enable specific alerts by product/family + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if contains(["PostgreSQL", "MySQL"], alert.product_name) + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +Or use specific alert IDs: + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + preconfigured_alert_ids = [ + "6c6843af-1815-46df-9e52-6feafcf31fd7", # PostgreSQL Too Many Connections + "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7", # MySQL Too Many Connections + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +## Filtering Alerts + +### By Product Name + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "Kubernetes" +] +``` + +### By Product Family + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_family == "Managed Databases" +] +``` + +### Multiple Criteria + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_family == "Load Balancer" && alert.product_name == "LB" +] +``` + +## Important Notes + +### Behavioral Changes + +- **No automatic alerts**: Unlike `enable_managed_alerts = true`, the API will not automatically enable additional alerts +- **Explicit configuration**: You must explicitly list all alerts you want to enable +- **State accuracy**: Terraform state will only track alerts you've configured + +### Compatibility + +- The deprecated `enable_managed_alerts` field will be removed in a future major version +- Both fields can coexist during migration, but `preconfigured_alert_ids` takes precedence +- If neither field is specified, no preconfigured alerts will be enabled + +## Troubleshooting + +### "Insufficient permissions" Error + +If you see permission errors when using the `scaleway_cockpit_preconfigured_alert` data source, ensure your IAM policy includes: + +```json +{ + "permission_sets": [ + { + "name": "CockpitManager", + "permissions": [ + "read:cockpit" + ] + } + ] +} +``` + +### Unexpected State Changes + +If Terraform shows unexpected changes to `preconfigured_alert_ids`: + +1. Verify the alert IDs still exist by querying the data source +2. Check that alerts are in `enabled` or `enabling` state +3. Ensure no manual changes were made outside Terraform + +## Additional Resources + +- [Cockpit Alert Manager Resource Documentation](../resources/cockpit_alert_manager.md) +- [Cockpit Preconfigured Alert Data Source Documentation](../data-sources/cockpit_preconfigured_alert.md) +- [Scaleway Cockpit Documentation](https://www.scaleway.com/en/docs/observability/cockpit/) + diff --git a/docs/resources/cockpit_alert_manager.md b/docs/resources/cockpit_alert_manager.md index 232063537..963d1c9bc 100644 --- a/docs/resources/cockpit_alert_manager.md +++ b/docs/resources/cockpit_alert_manager.md @@ -12,23 +12,48 @@ Refer to Cockpit's [product documentation](https://www.scaleway.com/en/docs/obse ## Example Usage -### Enable the alert manager and configure managed alerts +### Enable preconfigured alerts (Recommended) -The following commands allow you to: - -- enable the alert manager in a Project named `tf_test_project` -- enable [managed alerts](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#managed-alerts) -- set up [contact points](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#contact-points) to receive alert notifications +Use preconfigured alerts to monitor your Scaleway resources with ready-to-use alert rules: ```terraform +resource "scaleway_account_project" "project" { + name = "my-observability-project" +} + +resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id +} + +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_cockpit.main.project_id +} + +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_cockpit.main.project_id + + # Enable specific preconfigured alerts + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "instance" + ] + contact_points { + email = "alerts@example.com" + } +} +``` + +### Enable the alert manager with contact points + +```terraform resource "scaleway_account_project" "project" { name = "tf_test_project" } resource "scaleway_cockpit_alert_manager" "alert_manager" { - project_id = scaleway_account_project.project.id - enable_managed_alerts = true + project_id = scaleway_account_project.project.id contact_points { email = "alert1@example.com" @@ -40,13 +65,33 @@ resource "scaleway_cockpit_alert_manager" "alert_manager" { } ``` +### Legacy: Enable managed alerts (Deprecated) + +~> **Deprecated:** The `enable_managed_alerts` field is deprecated. Use `preconfigured_alert_ids` instead. + +```terraform +resource "scaleway_account_project" "project" { + name = "tf_test_project" +} + +resource "scaleway_cockpit_alert_manager" "alert_manager" { + project_id = scaleway_account_project.project.id + enable_managed_alerts = true + + contact_points { + email = "alert@example.com" + } +} +``` + ## Argument Reference This section lists the arguments that are supported: -- `enable_managed_alerts` - (Optional, Boolean) Specifies whether the alert manager should be enabled. Defaults to true. -- `contact_points` - (Optional, List of Map) A list of contact points with email addresses that will receive alerts. Each map should contain a single key email. +- `preconfigured_alert_ids` - (Optional, Set of String) A set of preconfigured alert rule IDs to enable explicitly. Use the [`scaleway_cockpit_preconfigured_alert`](../data-sources/cockpit_preconfigured_alert.md) data source to list available alerts. +- `enable_managed_alerts` - **Deprecated** (Optional, Boolean) Use `preconfigured_alert_ids` instead. This field will be removed in a future version. +- `contact_points` - (Optional, List of Map) A list of contact points with email addresses that will receive alerts. Each map should contain a single key `email`. - `project_id` - (Defaults to the Project ID specified in the [provider configuration](../index.md#project_id)) The ID of the Project the Cockpit is associated with. - `region` - (Defaults to the region specified in the [provider configuration](../index.md#arguments-reference)) The [region](../guides/regions_and_zones.md#regions) where the [alert manager](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#alert-manager) should be enabled. @@ -55,6 +100,7 @@ This section lists the arguments that are supported: In addition to all arguments above, the following attributes are exported: - `alert_manager_url` - The URL of the alert manager. +- `default_preconfigured_alert_ids` - (Set of String) A set of preconfigured alert rule IDs that are enabled automatically by the API when the alert manager is activated. This is a computed field that shows which alerts the API enables by default. ## Import diff --git a/internal/services/cockpit/alert_manager.go b/internal/services/cockpit/alert_manager.go index 2bc4d7f93..3db14ff9e 100644 --- a/internal/services/cockpit/alert_manager.go +++ b/internal/services/cockpit/alert_manager.go @@ -29,10 +29,22 @@ func ResourceCockpitAlertManager() *schema.Resource { "enable_managed_alerts": { Type: schema.TypeBool, Optional: true, - Default: true, - Description: "Enable or disable the alert manager", + Computed: true, + Deprecated: "Use 'preconfigured_alert_ids' instead. This field will be removed in a future version.", + Description: "Enable or disable the alert manager (deprecated)", + }, + "preconfigured_alert_ids": { + Type: schema.TypeSet, + Optional: true, + Description: "List of preconfigured alert rule IDs to enable explicitly. Use the scaleway_cockpit_preconfigured_alert data source to list available alerts.", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "default_preconfigured_alert_ids": { + Type: schema.TypeSet, + Computed: true, + Description: "List of preconfigured alert rule IDs enabled automatically by the API when alert manager is activated.", + Elem: &schema.Schema{Type: schema.TypeString}, }, - "contact_points": { Type: schema.TypeList, Optional: true, @@ -66,23 +78,30 @@ func ResourceCockpitAlertManagerCreate(ctx context.Context, d *schema.ResourceDa projectID := d.Get("project_id").(string) contactPoints := d.Get("contact_points").([]any) - EnableManagedAlerts := d.Get("enable_managed_alerts").(bool) _, err = api.EnableAlertManager(&cockpit.RegionalAPIEnableAlertManagerRequest{ Region: region, ProjectID: projectID, - }) + }, scw.WithContext(ctx)) if err != nil { return diag.FromErr(err) } - if EnableManagedAlerts { - _, err = api.EnableManagedAlerts(&cockpit.RegionalAPIEnableManagedAlertsRequest{ - Region: region, - ProjectID: projectID, - }) - if err != nil { - return diag.FromErr(err) + // Handle preconfigured alerts + if v, ok := d.GetOk("preconfigured_alert_ids"); ok { + alertIDs := expandStringSet(v.(*schema.Set)) + if len(alertIDs) > 0 { + _, err = api.EnableAlertRules(&cockpit.RegionalAPIEnableAlertRulesRequest{ + Region: region, + ProjectID: projectID, + RuleIDs: alertIDs, + }, scw.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + + // Note: Waiting for alerts to be enabled will be handled by SDK waiters when available + // For now, we continue without waiting as the Read function handles enabling/enabled states } } @@ -138,11 +157,69 @@ func ResourceCockpitAlertManagerRead(ctx context.Context, d *schema.ResourceData return diag.FromErr(err) } - _ = d.Set("enable_managed_alerts", alertManager.ManagedAlertsEnabled) - _ = d.Set("region", alertManager.Region) + // Note: We don't set "enable_managed_alerts" here because it's automatically + // managed by the API when preconfigured alerts are enabled/disabled. + // Setting it would cause perpetual drift. + _ = d.Set("region", string(alertManager.Region)) _ = d.Set("alert_manager_url", alertManager.AlertManagerURL) _ = d.Set("project_id", projectID) + // Get enabled preconfigured alerts and separate user-requested vs API-default alerts + // Handle permission errors gracefully to allow the resource to work without cockpit read permissions + var userRequestedIDs []string + var defaultEnabledIDs []string + + alerts, err := api.ListAlerts(&cockpit.RegionalAPIListAlertsRequest{ + Region: region, + ProjectID: projectID, + IsPreconfigured: scw.BoolPtr(true), + }, scw.WithContext(ctx), scw.WithAllPages()) + if err != nil { + // If we can't read alerts (e.g., permission denied), just set empty arrays + // This allows the resource to work even without cockpit read permissions + _ = d.Set("preconfigured_alert_ids", userRequestedIDs) + _ = d.Set("default_preconfigured_alert_ids", defaultEnabledIDs) + } else { + // Build a map of alert statuses + alertStatusMap := make(map[string]cockpit.AlertStatus) + for _, alert := range alerts.Alerts { + if alert.PreconfiguredData != nil && alert.PreconfiguredData.PreconfiguredRuleID != "" { + alertStatusMap[alert.PreconfiguredData.PreconfiguredRuleID] = alert.RuleStatus + } + } + + if v, ok := d.GetOk("preconfigured_alert_ids"); ok { + requestedIDs := expandStringSet(v.(*schema.Set)) + requestedMap := make(map[string]bool) + for _, id := range requestedIDs { + requestedMap[id] = true + } + + // Check all enabled/enabling alerts + for ruleID, status := range alertStatusMap { + if status == cockpit.AlertStatusEnabled || status == cockpit.AlertStatusEnabling { + if requestedMap[ruleID] { + // This alert was explicitly requested by the user + userRequestedIDs = append(userRequestedIDs, ruleID) + } else { + // This alert was enabled automatically by the API + defaultEnabledIDs = append(defaultEnabledIDs, ruleID) + } + } + } + } else { + // No alerts explicitly requested, all enabled alerts are API defaults + for ruleID, status := range alertStatusMap { + if status == cockpit.AlertStatusEnabled || status == cockpit.AlertStatusEnabling { + defaultEnabledIDs = append(defaultEnabledIDs, ruleID) + } + } + } + + _ = d.Set("preconfigured_alert_ids", userRequestedIDs) + _ = d.Set("default_preconfigured_alert_ids", defaultEnabledIDs) + } + contactPoints, err := api.ListContactPoints(&cockpit.RegionalAPIListContactPointsRequest{ Region: region, ProjectID: projectID, @@ -179,22 +256,39 @@ func ResourceCockpitAlertManagerUpdate(ctx context.Context, d *schema.ResourceDa return diag.FromErr(err) } - if d.HasChange("enable_managed_alerts") { - enable := d.Get("enable_managed_alerts").(bool) - if enable { - _, err = api.EnableManagedAlerts(&cockpit.RegionalAPIEnableManagedAlertsRequest{ + if d.HasChange("preconfigured_alert_ids") { + oldIDs, newIDs := d.GetChange("preconfigured_alert_ids") + oldSet := oldIDs.(*schema.Set) + newSet := newIDs.(*schema.Set) + + // IDs to disable: in old but not in new + toDisable := expandStringSet(oldSet.Difference(newSet)) + if len(toDisable) > 0 { + _, err = api.DisableAlertRules(&cockpit.RegionalAPIDisableAlertRulesRequest{ Region: region, ProjectID: projectID, - }) - } else { - _, err = api.DisableManagedAlerts(&cockpit.RegionalAPIDisableManagedAlertsRequest{ + RuleIDs: toDisable, + }, scw.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + + // Note: Waiting for alerts to be disabled will be handled by SDK waiters when available + } + + // IDs to enable: in new but not in old + toEnable := expandStringSet(newSet.Difference(oldSet)) + if len(toEnable) > 0 { + _, err = api.EnableAlertRules(&cockpit.RegionalAPIEnableAlertRulesRequest{ Region: region, ProjectID: projectID, + RuleIDs: toEnable, }, scw.WithContext(ctx)) - } + if err != nil { + return diag.FromErr(err) + } - if err != nil { - return diag.FromErr(err) + // Note: Waiting for alerts to be enabled will be handled by SDK waiters when available } } @@ -263,6 +357,21 @@ func ResourceCockpitAlertManagerDelete(ctx context.Context, d *schema.ResourceDa return diag.FromErr(err) } + // Disable all preconfigured alerts if any are enabled + if v, ok := d.GetOk("preconfigured_alert_ids"); ok { + alertIDs := expandStringSet(v.(*schema.Set)) + if len(alertIDs) > 0 { + _, err = api.DisableAlertRules(&cockpit.RegionalAPIDisableAlertRulesRequest{ + Region: region, + ProjectID: projectID, + RuleIDs: alertIDs, + }, scw.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + } + } + contactPoints, err := api.ListContactPoints(&cockpit.RegionalAPIListContactPointsRequest{ Region: region, ProjectID: projectID, @@ -284,18 +393,10 @@ func ResourceCockpitAlertManagerDelete(ctx context.Context, d *schema.ResourceDa } } - _, err = api.DisableManagedAlerts(&cockpit.RegionalAPIDisableManagedAlertsRequest{ - Region: region, - ProjectID: projectID, - }, scw.WithContext(ctx)) - if err != nil { - return diag.FromErr(err) - } - _, err = api.DisableAlertManager(&cockpit.RegionalAPIDisableAlertManagerRequest{ Region: region, ProjectID: projectID, - }) + }, scw.WithContext(ctx)) if err != nil { return diag.FromErr(err) } @@ -321,3 +422,12 @@ func ResourceCockpitAlertManagerParseID(resourceID string) (region scw.Region, p return scw.Region(parts[0]), parts[1], nil } + +func expandStringSet(set *schema.Set) []string { + result := make([]string, set.Len()) + for i, v := range set.List() { + result[i] = v.(string) + } + + return result +} diff --git a/internal/services/cockpit/alert_manager_test.go b/internal/services/cockpit/alert_manager_test.go index 53043d109..7e5fe7d8b 100644 --- a/internal/services/cockpit/alert_manager_test.go +++ b/internal/services/cockpit/alert_manager_test.go @@ -30,7 +30,6 @@ func TestAccCockpitAlertManager_CreateWithSingleContact(t *testing.T) { }), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "project_id"), - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "initial@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "alert_manager_url"), @@ -42,7 +41,6 @@ func TestAccCockpitAlertManager_CreateWithSingleContact(t *testing.T) { {"email": "updated@example.com"}, }), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "updated@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "alert_manager_url"), @@ -68,7 +66,6 @@ func TestAccCockpitAlertManager_CreateWithMultipleContacts(t *testing.T) { }), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "project_id"), - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "initial1@example.com"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.1.email", "initial2@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), @@ -82,7 +79,6 @@ func TestAccCockpitAlertManager_CreateWithMultipleContacts(t *testing.T) { {"email": "updated2@example.com"}, }), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "updated1@example.com"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.1.email", "updated2@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), @@ -109,7 +105,6 @@ func TestAccCockpitAlertManager_UpdateSingleContact(t *testing.T) { }), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "project_id"), - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "notupdated@example.com"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.1.email", "initial1@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), @@ -123,7 +118,6 @@ func TestAccCockpitAlertManager_UpdateSingleContact(t *testing.T) { {"email": "updated1@example.com"}, }), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.0.email", "notupdated@example.com"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "contact_points.1.email", "updated1@example.com"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), @@ -148,7 +142,6 @@ func TestAccCockpitAlertManager_EnableDisable(t *testing.T) { { Config: testAccCockpitAlertManagerEnableConfig(true), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.alert_manager", "enable_managed_alerts", "true"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "region"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.alert_manager", "alert_manager_url"), testAccCheckAlertManagerEnabled(tt, "scaleway_cockpit_alert_manager.alert_manager", true), @@ -181,7 +174,6 @@ func TestAccCockpitAlertManager_IDHandling(t *testing.T) { resource "scaleway_cockpit_alert_manager" "main" { project_id = scaleway_account_project.project.id - enable_managed_alerts = true contact_points { email = "test@example.com" @@ -192,7 +184,6 @@ func TestAccCockpitAlertManager_IDHandling(t *testing.T) { resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "id"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "project_id"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "region"), - resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "enable_managed_alerts", "true"), resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "alert_manager_url"), resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "contact_points.0.email", "test@example.com"), testAccCheckAlertManagerIDFormat(tt, "scaleway_cockpit_alert_manager.main"), @@ -206,7 +197,6 @@ func TestAccCockpitAlertManager_IDHandling(t *testing.T) { resource "scaleway_cockpit_alert_manager" "main" { project_id = scaleway_account_project.project.id - enable_managed_alerts = true contact_points { email = "updated@example.com" @@ -241,7 +231,6 @@ func testAccCockpitAlertManagerConfigWithContacts(contactPoints []map[string]str resource "scaleway_cockpit_alert_manager" "alert_manager" { project_id = scaleway_account_project.project.id - enable_managed_alerts = true %s } `, contactsConfig) @@ -390,3 +379,179 @@ func testAccCheckAlertManagerIDFormat(tt *acctest.TestTools, resourceName string return nil } } + +func TestAccCockpitAlertManager_WithPreconfiguredAlerts(t *testing.T) { + tt := acctest.NewTestTools(t) + defer tt.Cleanup() + + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: tt.ProviderFactories, + CheckDestroy: testAccCockpitAlertManagerAndContactsDestroy(tt), + Steps: []resource.TestStep{ + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_alert_preconfigured" + } + + resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Enable 2 specific preconfigured alerts (stable IDs) + preconfigured_alert_ids = [ + "6c6843af-1815-46df-9e52-6feafcf31fd7", # PostgreSQL Too Many Connections + "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7" # MySQL Too Many Connections + ] + + contact_points { + email = "test@example.com" + } + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "project_id"), + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "region"), + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "alert_manager_url"), + resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "contact_points.0.email", "test@example.com"), + resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.#", "2"), + resource.TestCheckTypeSetElemAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.*", "6c6843af-1815-46df-9e52-6feafcf31fd7"), + resource.TestCheckTypeSetElemAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.*", "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7"), + // Check that default alerts may be present (computed field) + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "default_preconfigured_alert_ids.#"), + ), + }, + }, + }) +} + +func TestAccCockpitAlertManager_UpdatePreconfiguredAlerts(t *testing.T) { + tt := acctest.NewTestTools(t) + defer tt.Cleanup() + + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: tt.ProviderFactories, + CheckDestroy: testAccCockpitAlertManagerAndContactsDestroy(tt), + Steps: []resource.TestStep{ + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_alert_update" + } + + resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Enable a specific PostgreSQL alert (stable ID) + preconfigured_alert_ids = [ + "6c6843af-1815-46df-9e52-6feafcf31fd7" # PostgreSQL Too Many Connections + ] + + contact_points { + email = "test@example.com" + } + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "project_id"), + testAccCheckPreconfiguredAlertsCount(tt, "scaleway_cockpit_alert_manager.main", 1), + resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.#", "1"), + resource.TestCheckTypeSetElemAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.*", "6c6843af-1815-46df-9e52-6feafcf31fd7"), + // Check that default alerts may be present (computed field) + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "default_preconfigured_alert_ids.#"), + ), + }, + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_alert_update" + } + + resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Enable 2 specific alerts (stable IDs) + preconfigured_alert_ids = [ + "6c6843af-1815-46df-9e52-6feafcf31fd7", # PostgreSQL Too Many Connections + "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7" # MySQL Too Many Connections + ] + + contact_points { + email = "test@example.com" + } + } + `, + Check: resource.ComposeTestCheckFunc( + testAccCheckPreconfiguredAlertsCount(tt, "scaleway_cockpit_alert_manager.main", 2), + resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.#", "2"), + resource.TestCheckTypeSetElemAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.*", "6c6843af-1815-46df-9e52-6feafcf31fd7"), + resource.TestCheckTypeSetElemAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.*", "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7"), + // Check that default alerts may be present (computed field) + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "default_preconfigured_alert_ids.#"), + ), + }, + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_alert_update" + } + + resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Disable all + preconfigured_alert_ids = [] + + contact_points { + email = "test@example.com" + } + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("scaleway_cockpit_alert_manager.main", "preconfigured_alert_ids.#", "0"), + testAccCheckPreconfiguredAlertsCount(tt, "scaleway_cockpit_alert_manager.main", 0), + // Default alerts might still be present even when user requests none + resource.TestCheckResourceAttrSet("scaleway_cockpit_alert_manager.main", "default_preconfigured_alert_ids.#"), + ), + }, + }, + }) +} + +func testAccCheckPreconfiguredAlertsCount(tt *acctest.TestTools, resourceName string, expectedCount int) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return errors.New("alert manager not found: " + resourceName) + } + + api := cockpit.NewRegionalAPI(meta.ExtractScwClient(tt.Meta)) + projectID := rs.Primary.Attributes["project_id"] + region := scw.Region(rs.Primary.Attributes["region"]) + + // List all preconfigured alerts + alerts, err := api.ListAlerts(&cockpit.RegionalAPIListAlertsRequest{ + Region: region, + ProjectID: projectID, + IsPreconfigured: scw.BoolPtr(true), + }, scw.WithAllPages()) + if err != nil { + return err + } + + // Count alerts that are enabled or enabling + enabledCount := 0 + for _, alert := range alerts.Alerts { + if alert.PreconfiguredData != nil { + if alert.RuleStatus == cockpit.AlertStatusEnabled || alert.RuleStatus == cockpit.AlertStatusEnabling { + enabledCount++ + } + } + } + + if enabledCount != expectedCount { + return fmt.Errorf("expected %d enabled preconfigured alerts, got %d", expectedCount, enabledCount) + } + + return nil + } +} diff --git a/internal/services/cockpit/preconfigured_alert_data_source.go b/internal/services/cockpit/preconfigured_alert_data_source.go new file mode 100644 index 000000000..f42792da0 --- /dev/null +++ b/internal/services/cockpit/preconfigured_alert_data_source.go @@ -0,0 +1,175 @@ +package cockpit + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + cockpit "github.com/scaleway/scaleway-sdk-go/api/cockpit/v1" + "github.com/scaleway/scaleway-sdk-go/scw" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/verify" +) + +func DataSourceCockpitPreconfiguredAlert() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceCockpitPreconfiguredAlertRead, + Schema: map[string]*schema.Schema{ + "project_id": account.ProjectIDSchema(), + "region": regional.Schema(), + "alerts": { + Type: schema.TypeList, + Computed: true, + Description: "List of preconfigured alerts", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the alert", + }, + "rule": { + Type: schema.TypeString, + Computed: true, + Description: "PromQL rule defining the alert condition", + }, + "duration": { + Type: schema.TypeString, + Computed: true, + Description: "Duration for which the alert must be active before firing", + }, + "rule_status": { + Type: schema.TypeString, + Computed: true, + Description: "Status of the alert (enabled, disabled, enabling, disabling)", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "Current state of the alert (inactive, pending, firing)", + }, + "annotations": { + Type: schema.TypeMap, + Computed: true, + Description: "Annotations for the alert", + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "preconfigured_rule_id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the preconfigured rule", + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + Description: "Human readable name of the alert", + }, + "display_description": { + Type: schema.TypeString, + Computed: true, + Description: "Human readable description of the alert", + }, + "product_name": { + Type: schema.TypeString, + Computed: true, + Description: "Product associated with the alert", + }, + "product_family": { + Type: schema.TypeString, + Computed: true, + Description: "Family of the product associated with the alert", + }, + "data_source_id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the data source containing the alert rule", + }, + }, + }, + }, + "data_source_id": { + Type: schema.TypeString, + Optional: true, + Description: "Filter alerts by data source ID", + ValidateDiagFunc: verify.IsUUID(), + }, + "rule_status": { + Type: schema.TypeString, + Optional: true, + Description: "Filter alerts by rule status (enabled, disabled)", + ValidateDiagFunc: verify.ValidateEnum[cockpit.AlertStatus](), + }, + }, + } +} + +func dataSourceCockpitPreconfiguredAlertRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { + api, region, err := cockpitAPIWithRegion(d, m) + if err != nil { + return diag.FromErr(err) + } + + projectID := d.Get("project_id").(string) + if projectID == "" { + defaultProjectID, err := getDefaultProjectID(ctx, m) + if err != nil { + return diag.FromErr(err) + } + projectID = defaultProjectID + } + + req := &cockpit.RegionalAPIListAlertsRequest{ + Region: region, + ProjectID: projectID, + IsPreconfigured: scw.BoolPtr(true), + } + + if dataSourceID, ok := d.GetOk("data_source_id"); ok { + req.DataSourceID = scw.StringPtr(dataSourceID.(string)) + } + + if ruleStatus, ok := d.GetOk("rule_status"); ok { + status := cockpit.AlertStatus(ruleStatus.(string)) + req.RuleStatus = &status + } + + response, err := api.ListAlerts(req, scw.WithContext(ctx), scw.WithAllPages()) + if err != nil { + return diag.FromErr(err) + } + + alerts := make([]map[string]any, 0, len(response.Alerts)) + for _, alert := range response.Alerts { + alertMap := map[string]any{ + "name": alert.Name, + "rule": alert.Rule, + "duration": alert.Duration, + "rule_status": string(alert.RuleStatus), + "annotations": alert.Annotations, + "data_source_id": alert.DataSourceID, + } + + if alert.State != nil { + alertMap["state"] = string(*alert.State) + } + + if alert.PreconfiguredData != nil { + alertMap["preconfigured_rule_id"] = alert.PreconfiguredData.PreconfiguredRuleID + alertMap["display_name"] = alert.PreconfiguredData.DisplayName + alertMap["display_description"] = alert.PreconfiguredData.DisplayDescription + alertMap["product_name"] = alert.PreconfiguredData.ProductName + alertMap["product_family"] = alert.PreconfiguredData.ProductFamily + } + + alerts = append(alerts, alertMap) + } + + d.SetId(regional.NewIDString(region, projectID)) + _ = d.Set("project_id", projectID) + _ = d.Set("region", string(region)) + _ = d.Set("alerts", alerts) + + return nil +} + diff --git a/internal/services/cockpit/preconfigured_alert_data_source_test.go b/internal/services/cockpit/preconfigured_alert_data_source_test.go new file mode 100644 index 000000000..7039e76c3 --- /dev/null +++ b/internal/services/cockpit/preconfigured_alert_data_source_test.go @@ -0,0 +1,74 @@ +package cockpit_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest" +) + +func TestAccDataSourceCockpitPreconfiguredAlert_Basic(t *testing.T) { + tt := acctest.NewTestTools(t) + defer tt.Cleanup() + + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: tt.ProviderFactories, + Steps: []resource.TestStep{ + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_preconfigured_alert_ds" + } + + resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id + } + + data "scaleway_cockpit_preconfigured_alert" "main" { + project_id = scaleway_cockpit.main.project_id + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair("data.scaleway_cockpit_preconfigured_alert.main", "project_id", "scaleway_account_project.project", "id"), + resource.TestCheckResourceAttrSet("data.scaleway_cockpit_preconfigured_alert.main", "alerts.#"), + ), + }, + }, + }) +} + +func TestAccDataSourceCockpitPreconfiguredAlert_WithFilters(t *testing.T) { + tt := acctest.NewTestTools(t) + defer tt.Cleanup() + + resource.ParallelTest(t, resource.TestCase{ + ProtoV6ProviderFactories: tt.ProviderFactories, + Steps: []resource.TestStep{ + { + Config: ` + resource "scaleway_account_project" "project" { + name = "tf_tests_cockpit_preconfigured_alert_filters" + } + + resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id + } + + data "scaleway_cockpit_preconfigured_alert" "enabled" { + project_id = scaleway_cockpit.main.project_id + rule_status = "enabled" + } + + data "scaleway_cockpit_preconfigured_alert" "disabled" { + project_id = scaleway_cockpit.main.project_id + rule_status = "disabled" + } + `, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.scaleway_cockpit_preconfigured_alert.enabled", "alerts.#"), + resource.TestCheckResourceAttrSet("data.scaleway_cockpit_preconfigured_alert.disabled", "alerts.#"), + ), + }, + }, + }) +} diff --git a/provider/sdkv2.go b/provider/sdkv2.go index d37440ca1..c6e17e9a6 100644 --- a/provider/sdkv2.go +++ b/provider/sdkv2.go @@ -274,6 +274,7 @@ func SDKProvider(config *Config) plugin.ProviderFunc { "scaleway_block_volume": block.DataSourceVolume(), "scaleway_cockpit": cockpit.DataSourceCockpit(), "scaleway_cockpit_grafana": cockpit.DataSourceCockpitGrafana(), + "scaleway_cockpit_preconfigured_alert": cockpit.DataSourceCockpitPreconfiguredAlert(), "scaleway_cockpit_source": cockpit.DataSourceCockpitSource(), "scaleway_cockpit_sources": cockpit.DataSourceCockpitSources(), "scaleway_config": scwconfig.DataSourceConfig(), diff --git a/templates/data-sources/cockpit_preconfigured_alert.md.tmpl b/templates/data-sources/cockpit_preconfigured_alert.md.tmpl new file mode 100644 index 000000000..c3f84efa9 --- /dev/null +++ b/templates/data-sources/cockpit_preconfigured_alert.md.tmpl @@ -0,0 +1,100 @@ +--- +subcategory: "Cockpit" +page_title: "Scaleway: scaleway_cockpit_preconfigured_alert" +--- + +# Data Source: scaleway_cockpit_preconfigured_alert + +Gets information about preconfigured alert rules available in Scaleway Cockpit. + +Preconfigured alerts are ready-to-use alert rules that monitor common metrics for Scaleway services. +You can enable these alerts in your Alert Manager using the `scaleway_cockpit_alert_manager` resource. + +For more information, refer to Cockpit's [product documentation](https://www.scaleway.com/en/docs/observability/cockpit/concepts/) and [API documentation](https://www.scaleway.com/en/developers/api/cockpit/regional-api). + +## Example Usage + +### Basic usage + +```terraform +data "scaleway_cockpit_preconfigured_alert" "main" { + project_id = scaleway_account_project.project.id +} + +output "available_alerts" { + value = data.scaleway_cockpit_preconfigured_alert.main.alerts +} +``` + +### Filter by status + +```terraform +data "scaleway_cockpit_preconfigured_alert" "enabled" { + project_id = scaleway_account_project.project.id + rule_status = "enabled" +} + +data "scaleway_cockpit_preconfigured_alert" "disabled" { + project_id = scaleway_account_project.project.id + rule_status = "disabled" +} +``` + +### Use with Alert Manager + +```terraform +resource "scaleway_account_project" "project" { + name = "my-observability-project" +} + +resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id +} + +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_cockpit.main.project_id +} + +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_cockpit.main.project_id + + # Enable specific alerts by their preconfigured_rule_id + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "instance" && alert.rule_status == "disabled" + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +## Argument Reference + +- `project_id` - (Optional) The ID of the project the alerts are associated with. If not provided, the default project configured in the provider is used. +- `region` - (Optional, defaults to provider region) The region in which the alerts exist. +- `data_source_id` - (Optional) Filter alerts by data source ID. +- `rule_status` - (Optional) Filter alerts by rule status. Valid values are `enabled` or `disabled`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +- `id` - The ID of the resource (project ID with region). +- `alerts` - List of preconfigured alerts. Each alert contains: + - `name` - Name of the alert rule. + - `rule` - PromQL expression defining the alert condition. + - `duration` - Duration for which the condition must be true before the alert fires (e.g., "5m"). + - `rule_status` - Status of the alert rule (`enabled`, `disabled`, `enabling`, `disabling`). + - `state` - Current state of the alert (`inactive`, `pending`, `firing`). + - `annotations` - Map of annotations attached to the alert. + - `preconfigured_rule_id` - Unique identifier of the preconfigured rule. Use this ID in `scaleway_cockpit_alert_manager` resource. + - `display_name` - Human-readable name of the alert. + - `display_description` - Human-readable description of the alert. + - `product_name` - Scaleway product associated with the alert (e.g., "instance", "rdb", "kubernetes"). + - `product_family` - Family of the product (e.g., "compute", "storage", "network"). + - `data_source_id` - ID of the data source containing the alert rule. + + diff --git a/templates/guides/migration_guide_cockpit_alert_manager.md b/templates/guides/migration_guide_cockpit_alert_manager.md new file mode 100644 index 000000000..396fe44c6 --- /dev/null +++ b/templates/guides/migration_guide_cockpit_alert_manager.md @@ -0,0 +1,166 @@ +--- +page_title: "Cockpit Alert Manager Migration Guide" +--- + +# Cockpit Alert Manager Migration Guide + +This guide explains how to migrate from the deprecated `enable_managed_alerts` field to the new `preconfigured_alert_ids` field in the `scaleway_cockpit_alert_manager` resource. + +## Background + +The `enable_managed_alerts` field is being deprecated in favor of a more flexible approach using `preconfigured_alert_ids`. This change provides: + +- **Granular control**: Select specific alerts instead of enabling all managed alerts +- **Better visibility**: Explicitly declare which alerts are enabled in your Terraform configuration +- **Improved state management**: Terraform accurately tracks which alerts are active + +## Migration Steps + +### Before Migration (Deprecated) + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + enable_managed_alerts = true + + contact_points { + email = "alerts@example.com" + } +} +``` + +### After Migration (Recommended) + +#### Step 1: List Available Preconfigured Alerts + +Use the data source to discover available alerts: + +```terraform +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_account_project.project.id +} + +output "available_alerts" { + value = data.scaleway_cockpit_preconfigured_alert.all.alerts +} +``` + +Run `terraform apply` and review the output to see available alerts. + +#### Step 2: Select Specific Alerts + +Choose the alerts you want to enable: + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + # Enable specific alerts by product/family + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if contains(["PostgreSQL", "MySQL"], alert.product_name) + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +Or use specific alert IDs: + +```terraform +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_account_project.project.id + + preconfigured_alert_ids = [ + "6c6843af-1815-46df-9e52-6feafcf31fd7", # PostgreSQL Too Many Connections + "eb8a941e-698d-47d6-b62d-4b6c13f7b4b7", # MySQL Too Many Connections + ] + + contact_points { + email = "alerts@example.com" + } +} +``` + +## Filtering Alerts + +### By Product Name + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "Kubernetes" +] +``` + +### By Product Family + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_family == "Managed Databases" +] +``` + +### Multiple Criteria + +```terraform +preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_family == "Load Balancer" && alert.product_name == "LB" +] +``` + +## Important Notes + +### Behavioral Changes + +- **No automatic alerts**: Unlike `enable_managed_alerts = true`, the API will not automatically enable additional alerts +- **Explicit configuration**: You must explicitly list all alerts you want to enable +- **State accuracy**: Terraform state will only track alerts you've configured + +### Compatibility + +- The deprecated `enable_managed_alerts` field will be removed in a future major version +- Both fields can coexist during migration, but `preconfigured_alert_ids` takes precedence +- If neither field is specified, no preconfigured alerts will be enabled + +## Troubleshooting + +### "Insufficient permissions" Error + +If you see permission errors when using the `scaleway_cockpit_preconfigured_alert` data source, ensure your IAM policy includes: + +```json +{ + "permission_sets": [ + { + "name": "CockpitManager", + "permissions": [ + "read:cockpit" + ] + } + ] +} +``` + +### Unexpected State Changes + +If Terraform shows unexpected changes to `preconfigured_alert_ids`: + +1. Verify the alert IDs still exist by querying the data source +2. Check that alerts are in `enabled` or `enabling` state +3. Ensure no manual changes were made outside Terraform + +## Additional Resources + +- [Cockpit Alert Manager Resource Documentation](../resources/cockpit_alert_manager.md) +- [Cockpit Preconfigured Alert Data Source Documentation](../data-sources/cockpit_preconfigured_alert.md) +- [Scaleway Cockpit Documentation](https://www.scaleway.com/en/docs/observability/cockpit/) + diff --git a/templates/resources/cockpit_alert_manager.md.tmpl b/templates/resources/cockpit_alert_manager.md.tmpl index 0b20f6a8f..4299033a6 100644 --- a/templates/resources/cockpit_alert_manager.md.tmpl +++ b/templates/resources/cockpit_alert_manager.md.tmpl @@ -13,23 +13,48 @@ Refer to Cockpit's [product documentation](https://www.scaleway.com/en/docs/obse ## Example Usage -### Enable the alert manager and configure managed alerts +### Enable preconfigured alerts (Recommended) -The following commands allow you to: - -- enable the alert manager in a Project named `tf_test_project` -- enable [managed alerts](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#managed-alerts) -- set up [contact points](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#contact-points) to receive alert notifications +Use preconfigured alerts to monitor your Scaleway resources with ready-to-use alert rules: ```terraform +resource "scaleway_account_project" "project" { + name = "my-observability-project" +} + +resource "scaleway_cockpit" "main" { + project_id = scaleway_account_project.project.id +} + +data "scaleway_cockpit_preconfigured_alert" "all" { + project_id = scaleway_cockpit.main.project_id +} + +resource "scaleway_cockpit_alert_manager" "main" { + project_id = scaleway_cockpit.main.project_id + + # Enable specific preconfigured alerts + preconfigured_alert_ids = [ + for alert in data.scaleway_cockpit_preconfigured_alert.all.alerts : + alert.preconfigured_rule_id + if alert.product_name == "instance" + ] + contact_points { + email = "alerts@example.com" + } +} +``` + +### Enable the alert manager with contact points + +```terraform resource "scaleway_account_project" "project" { name = "tf_test_project" } resource "scaleway_cockpit_alert_manager" "alert_manager" { - project_id = scaleway_account_project.project.id - enable_managed_alerts = true + project_id = scaleway_account_project.project.id contact_points { email = "alert1@example.com" @@ -41,13 +66,33 @@ resource "scaleway_cockpit_alert_manager" "alert_manager" { } ``` +### Legacy: Enable managed alerts (Deprecated) + +~> **Deprecated:** The `enable_managed_alerts` field is deprecated. Use `preconfigured_alert_ids` instead. + +```terraform +resource "scaleway_account_project" "project" { + name = "tf_test_project" +} + +resource "scaleway_cockpit_alert_manager" "alert_manager" { + project_id = scaleway_account_project.project.id + enable_managed_alerts = true + + contact_points { + email = "alert@example.com" + } +} +``` + ## Argument Reference This section lists the arguments that are supported: -- `enable_managed_alerts` - (Optional, Boolean) Specifies whether the alert manager should be enabled. Defaults to true. -- `contact_points` - (Optional, List of Map) A list of contact points with email addresses that will receive alerts. Each map should contain a single key email. +- `preconfigured_alert_ids` - (Optional, Set of String) A set of preconfigured alert rule IDs to enable explicitly. Use the [`scaleway_cockpit_preconfigured_alert`](../data-sources/cockpit_preconfigured_alert.md) data source to list available alerts. +- `enable_managed_alerts` - **Deprecated** (Optional, Boolean) Use `preconfigured_alert_ids` instead. This field will be removed in a future version. +- `contact_points` - (Optional, List of Map) A list of contact points with email addresses that will receive alerts. Each map should contain a single key `email`. - `project_id` - (Defaults to the Project ID specified in the [provider configuration](../index.md#project_id)) The ID of the Project the Cockpit is associated with. - `region` - (Defaults to the region specified in the [provider configuration](../index.md#arguments-reference)) The [region](../guides/regions_and_zones.md#regions) where the [alert manager](https://www.scaleway.com/en/docs/observability/cockpit/concepts/#alert-manager) should be enabled. @@ -56,6 +101,7 @@ This section lists the arguments that are supported: In addition to all arguments above, the following attributes are exported: - `alert_manager_url` - The URL of the alert manager. +- `default_preconfigured_alert_ids` - (Set of String) A set of preconfigured alert rule IDs that are enabled automatically by the API when the alert manager is activated. This is a computed field that shows which alerts the API enables by default. ## Import