diff --git a/alert_channels.go b/alert_channels.go deleted file mode 100644 index 91b938875..000000000 --- a/alert_channels.go +++ /dev/null @@ -1,83 +0,0 @@ -package linodego - -import ( - "context" - "encoding/json" - "time" - - "github.com/linode/linodego/internal/parseabletime" -) - -type AlertNotificationType string - -const ( - EmailAlertNotification AlertNotificationType = "email" -) - -type AlertChannelType string - -const ( - SystemAlertChannel AlertChannelType = "system" - UserAlertChannel AlertChannelType = "user" -) - -// AlertChannelEnvelope represents a single alert channel entry returned inside alert definition -type AlertChannelEnvelope struct { - ID int `json:"id"` - Label string `json:"label"` - Type string `json:"type"` - URL string `json:"url"` -} - -// AlertChannel represents a Monitor Channel object. -type AlertChannel struct { - Alerts []AlertChannelEnvelope `json:"alerts"` - ChannelType AlertNotificationType `json:"channel_type"` - Content ChannelContent `json:"content"` - Created *time.Time `json:"-"` - CreatedBy string `json:"created_by"` - Updated *time.Time `json:"-"` - UpdatedBy string `json:"updated_by"` - ID int `json:"id"` - Label string `json:"label"` - Type AlertChannelType `json:"type"` -} - -type EmailChannelContent struct { - EmailAddresses []string `json:"email_addresses"` -} - -// ChannelContent represents the content block for an AlertChannel, which varies by channel type. -type ChannelContent struct { - Email *EmailChannelContent `json:"email"` - // Other channel types like 'webhook', 'slack' could be added here as optional fields. -} - -// UnmarshalJSON implements the json.Unmarshaler interface -func (a *AlertChannel) UnmarshalJSON(b []byte) error { - type Mask AlertChannel - - p := struct { - *Mask - - Updated *parseabletime.ParseableTime `json:"updated"` - Created *parseabletime.ParseableTime `json:"created"` - }{ - Mask: (*Mask)(a), - } - - if err := json.Unmarshal(b, &p); err != nil { - return err - } - - a.Updated = (*time.Time)(p.Updated) - a.Created = (*time.Time)(p.Created) - - return nil -} - -// ListAlertChannels gets a paginated list of Alert Channels. -func (c *Client) ListAlertChannels(ctx context.Context, opts *ListOptions) ([]AlertChannel, error) { - endpoint := formatAPIPath("monitor/alert-channels") - return getPaginatedResults[AlertChannel](ctx, c, endpoint, opts) -} diff --git a/monitor_alert_channels.go b/monitor_alert_channels.go new file mode 100644 index 000000000..0ba980f91 --- /dev/null +++ b/monitor_alert_channels.go @@ -0,0 +1,76 @@ +package linodego + +// @TODO: disable alert channel for now because API made breaking changes. +// type AlertNotificationType string +// +// const ( +// EmailAlertNotification AlertNotificationType = "email" +//) +// +// type AlertChannelType string +// +// const ( +// SystemAlertChannel AlertChannelType = "system" +// UserAlertChannel AlertChannelType = "user" +//) +// +//// AlertChannelEnvelope represents a single alert channel entry returned inside alert definition +// type AlertChannelEnvelope struct { +// ID int `json:"id"` +// Label string `json:"label"` +// Type string `json:"type"` +// URL string `json:"url"` +//} +// +//// AlertChannel represents a Monitor Channel object. +// type AlertChannel struct { +// Alerts []AlertChannelEnvelope `json:"alerts"` +// ChannelType AlertNotificationType `json:"channel_type"` +// Content ChannelContent `json:"content"` +// Created *time.Time `json:"-"` +// CreatedBy string `json:"created_by"` +// Updated *time.Time `json:"-"` +// UpdatedBy string `json:"updated_by"` +// ID int `json:"id"` +// Label string `json:"label"` +// Type AlertChannelType `json:"type"` +// } +// +// type EmailChannelContent struct { +// EmailAddresses []string `json:"email_addresses"` +// } +// +//// ChannelContent represents the content block for an AlertChannel, which varies by channel type. +// type ChannelContent struct { +// Email *EmailChannelContent `json:"email"` +// // Other channel types like 'webhook', 'slack' could be added here as optional fields. +// } +// +//// UnmarshalJSON implements the json.Unmarshaler interface +// func (a *AlertChannel) UnmarshalJSON(b []byte) error { +// type Mask AlertChannel +// +// p := struct { +// *Mask +// +// Updated *parseabletime.ParseableTime `json:"updated"` +// Created *parseabletime.ParseableTime `json:"created"` +// }{ +// Mask: (*Mask)(a), +// } +// +// if err := json.Unmarshal(b, &p); err != nil { +// return err +// } +// +// a.Updated = (*time.Time)(p.Updated) +// a.Created = (*time.Time)(p.Created) +// +// return nil +// } + +//// ListAlertChannels gets a paginated list of Alert Channels. +// func (c *Client) ListAlertChannels(ctx context.Context, opts *ListOptions) ([]AlertChannel, error) { +// endpoint := formatAPIPath("monitor/alert-channels") +// return getPaginatedResults[AlertChannel](ctx, c, endpoint, opts) +// } diff --git a/monitor_alert_definitions.go b/monitor_alert_definitions.go index a55293caa..8e4062f18 100644 --- a/monitor_alert_definitions.go +++ b/monitor_alert_definitions.go @@ -8,6 +8,15 @@ import ( "github.com/linode/linodego/internal/parseabletime" ) +type AlertDefinitionStatus string + +const ( + AlertDefinitionStatusEnabled AlertDefinitionStatus = "enabled" + AlertDefinitionStatusDisabled AlertDefinitionStatus = "disabled" + AlertDefinitionStatusInProgress AlertDefinitionStatus = "in_progress" + AlertDefinitionStatusFailed AlertDefinitionStatus = "failed" +) + // AlertDefinition represents an ACLP Alert Definition object type AlertDefinition struct { ID int `json:"id"` @@ -15,7 +24,7 @@ type AlertDefinition struct { Severity int `json:"severity"` Type string `json:"type"` ServiceType string `json:"service_type"` - Status string `json:"status"` + Status AlertDefinitionStatus `json:"status"` HasMoreResources bool `json:"has_more_resources"` RuleCriteria RuleCriteria `json:"rule_criteria"` TriggerConditions TriggerConditions `json:"trigger_conditions"` @@ -24,7 +33,7 @@ type AlertDefinition struct { Updated *time.Time `json:"-"` UpdatedBy string `json:"updated_by"` CreatedBy string `json:"created_by"` - EntityIDs []any `json:"entity_ids"` + EntityIDs []string `json:"entity_ids"` Description string `json:"description"` Class string `json:"class"` } @@ -89,6 +98,14 @@ type DimensionFilterOptions struct { Value string `json:"value,omitempty"` } +// AlertChannelEnvelope represents a single alert channel entry returned inside alert definition +type AlertChannelEnvelope struct { + ID int `json:"id"` + Label string `json:"label"` + Type string `json:"type"` + URL string `json:"url"` +} + // AlertType represents the type of alert: "user" or "system" type AlertType string @@ -128,13 +145,14 @@ type AlertDefinitionCreateOptions struct { // AlertDefinitionUpdateOptions are the options used to update an alert definition. type AlertDefinitionUpdateOptions struct { - Label string `json:"label"` - Severity int `json:"severity"` - ChannelIDs []int `json:"channel_ids"` - RuleCriteria *RuleCriteriaOptions `json:"rule_criteria,omitempty"` - TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` - EntityIDs []string `json:"entity_ids,omitempty"` - Description *string `json:"description,omitempty"` + Label string `json:"label"` + Severity int `json:"severity"` + ChannelIDs []int `json:"channel_ids"` + RuleCriteria *RuleCriteriaOptions `json:"rule_criteria,omitempty"` + TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` + EntityIDs []string `json:"entity_ids,omitempty"` + Description *string `json:"description,omitempty"` + Status *AlertDefinitionStatus `json:"status,omitempty"` } // UnmarshalJSON implements the json.Unmarshaler interface diff --git a/test/integration/fixtures/TestMonitorAlertChannels_List.yaml b/test/integration/fixtures/TestMonitorAlertChannels_List.yaml deleted file mode 100644 index ece9ea602..000000000 --- a/test/integration/fixtures/TestMonitorAlertChannels_List.yaml +++ /dev/null @@ -1,113 +0,0 @@ ---- -version: 1 -interactions: -- request: - body: "" - form: {} - headers: - Accept: - - application/json - Content-Type: - - application/json - User-Agent: - - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/monitor/alert-channels?page=1 - method: GET - response: - body: '{"pages": 1, "page": 1, "results": 1, "data": [{"id": 10000, "label": "Read-Write - Channel", "channel_type": "email", "type": "system", "content": {"email": {"email_addresses": - ["Users-with-read-write-access-to-resources"]}}, "alerts": [{"id": 10000, "label": - "High Memory Usage Plan Dedicated", "url": "/monitor/alerts-definitions/10000", - "type": "alerts-definitions"}, {"id": 10001, "label": "High Memory Usage Plan - Shared", "url": "/monitor/alerts-definitions/10001", "type": "alerts-definitions"}, - {"id": 10002, "label": "High CPU Usage Plan Dedicated", "url": "/monitor/alerts-definitions/10002", - "type": "alerts-definitions"}, {"id": 10003, "label": "High CPU Usage Plan Shared", - "url": "/monitor/alerts-definitions/10003", "type": "alerts-definitions"}, {"id": - 10004, "label": "High Disk Usage Plan Dedicated", "url": "/monitor/alerts-definitions/10004", - "type": "alerts-definitions"}, {"id": 10005, "label": "High Disk Usage Plan - Shared", "url": "/monitor/alerts-definitions/10005", "type": "alerts-definitions"}, - {"id": 10470, "label": "jgliahjs_updated", "url": "/monitor/alerts-definitions/10470", - "type": "alerts-definitions"}, {"id": 10503, "label": "obohragp-e2e-alert-1763051867-updated", - "url": "/monitor/alerts-definitions/10503", "type": "alerts-definitions"}, {"id": - 10504, "label": "lczvmpvu-e2e-alert-1763053808", "url": "/monitor/alerts-definitions/10504", - "type": "alerts-definitions"}, {"id": 10505, "label": "ocachcjp-e2e-alert-1763055498-updated", - "url": "/monitor/alerts-definitions/10505", "type": "alerts-definitions"}, {"id": - 10506, "label": "lrbbifob-e2e-alert-1763057283", "url": "/monitor/alerts-definitions/10506", - "type": "alerts-definitions"}, {"id": 10507, "label": "cbbrhfnv-e2e-alert-1763057415", - "url": "/monitor/alerts-definitions/10507", "type": "alerts-definitions"}, {"id": - 10508, "label": "gqzzxdgh-e2e-alert-1763057687", "url": "/monitor/alerts-definitions/10508", - "type": "alerts-definitions"}, {"id": 10509, "label": "smqsmhst-e2e-alert-1763058424", - "url": "/monitor/alerts-definitions/10509", "type": "alerts-definitions"}, {"id": - 10510, "label": "vygdkmkw-e2e-alert-1763058509", "url": "/monitor/alerts-definitions/10510", - "type": "alerts-definitions"}, {"id": 10511, "label": "dvlgyyvr-e2e-alert-1763058560", - "url": "/monitor/alerts-definitions/10511", "type": "alerts-definitions"}, {"id": - 10512, "label": "pscihptu-e2e-alert-1763058653-updated", "url": "/monitor/alerts-definitions/10512", - "type": "alerts-definitions"}, {"id": 10513, "label": "emxhsdlm-e2e-alert-1763059164-updated", - "url": "/monitor/alerts-definitions/10513", "type": "alerts-definitions"}, {"id": - 10514, "label": "fxbekqdl-e2e-alert-1763059845-updated", "url": "/monitor/alerts-definitions/10514", - "type": "alerts-definitions"}, {"id": 10529, "label": "Test Database Standby - Host", "url": "/monitor/alerts-definitions/10529", "type": "alerts-definitions"}, - {"id": 10623, "label": "go-test-alert-definition-idempotency-1766438857602066000", - "url": "/monitor/alerts-definitions/10623", "type": "alerts-definitions"}, {"id": - 10624, "label": "go-test-alert-definition-create-updated", "url": "/monitor/alerts-definitions/10624", - "type": "alerts-definitions"}, {"id": 10625, "label": "go-test-alert-definition-idempotency-1766439008803464000", - "url": "/monitor/alerts-definitions/10625", "type": "alerts-definitions"}, {"id": - 10626, "label": "go-test-alert-definition-create-updated", "url": "/monitor/alerts-definitions/10626", - "type": "alerts-definitions"}, {"id": 10627, "label": "go-test-alert-definition-idempotency-1766439204872603000", - "url": "/monitor/alerts-definitions/10627", "type": "alerts-definitions"}, {"id": - 10628, "label": "go-test-alert-definition-create-updated", "url": "/monitor/alerts-definitions/10628", - "type": "alerts-definitions"}, {"id": 10629, "label": "go-test-alert-definition-idempotency-1766439362488609000", - "url": "/monitor/alerts-definitions/10629", "type": "alerts-definitions"}, {"id": - 10630, "label": "go-test-alert-definition-create-updated", "url": "/monitor/alerts-definitions/10630", - "type": "alerts-definitions"}, {"id": 10631, "label": "go-test-alert-definition-idempotency-1766439501203284000", - "url": "/monitor/alerts-definitions/10631", "type": "alerts-definitions"}, {"id": - 10632, "label": "go-test-alert-definition-create-updated", "url": "/monitor/alerts-definitions/10632", - "type": "alerts-definitions"}], "created": "2018-01-02T03:04:05", "updated": - "2018-01-02T03:04:05", "created_by": "system", "updated_by": "system"}]}' - headers: - Access-Control-Allow-Credentials: - - "true" - Access-Control-Allow-Headers: - - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter - Access-Control-Allow-Methods: - - HEAD, GET, OPTIONS, POST, PUT, DELETE - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status - Akamai-Internal-Account: - - '*' - Cache-Control: - - max-age=0, no-cache, no-store - Connection: - - keep-alive - Content-Security-Policy: - - default-src 'none' - Content-Type: - - application/json - Expires: - - Mon, 22 Dec 2025 21:41:17 GMT - Pragma: - - no-cache - Strict-Transport-Security: - - max-age=31536000 - Vary: - - Authorization, X-Filter - - Authorization, X-Filter - - Accept-Encoding - X-Accepted-Oauth-Scopes: - - monitor:read_only - X-Content-Type-Options: - - nosniff - X-Frame-Options: - - DENY - - DENY - X-Oauth-Scopes: - - '*' - X-Ratelimit-Limit: - - "1840" - X-Xss-Protection: - - 1; mode=block - status: 200 OK - code: 200 - duration: "" diff --git a/test/integration/fixtures/TestMonitorAlertDefinition.yaml b/test/integration/fixtures/TestMonitorAlertDefinition.yaml index 813b2ca7e..129d38982 100644 --- a/test/integration/fixtures/TestMonitorAlertDefinition.yaml +++ b/test/integration/fixtures/TestMonitorAlertDefinition.yaml @@ -30,7 +30,7 @@ interactions: Plan Shared", "description": "Alert triggers when shared plan nodes consistently reach critical memory usage, risking application performance degradation.", "service_type": "dbaas", "type": "system", "scope": "entity", "class": "shared", - "regions": [], "status": "enabled", "entity_ids": [], "has_more_resources": + "regions": [], "status": "enabled", "entity_ids": ["389481", "395222"], "has_more_resources": false, "severity": 2, "rule_criteria": {"rules": [{"label": "Memory Usage", "metric": "memory_usage", "unit": "percent", "aggregate_function": "avg", "operator": "gt", "threshold": 90, "dimension_filters": []}]}, "alert_channels": [{"id": @@ -54,12 +54,12 @@ interactions: "description": "Alert triggers when shared plan nodes consistently exceed high CPU utilization, indicating potential performance issues.", "service_type": "dbaas", "type": "system", "scope": "entity", "class": "shared", "regions": - [], "status": "enabled", "entity_ids": [], "has_more_resources": false, "severity": - 2, "rule_criteria": {"rules": [{"label": "CPU Usage", "metric": "cpu_usage", - "unit": "percent", "aggregate_function": "avg", "operator": "gt", "threshold": - 90, "dimension_filters": []}]}, "alert_channels": [{"id": 10000, "label": "Read-Write - Channel", "url": "/monitor/alert-channels/10000", "type": "alert-channels"}], - "trigger_conditions": {"criteria_condition": "ALL", "polling_interval_seconds": + [], "status": "enabled", "entity_ids": ["389481", "395222"], "has_more_resources": + false, "severity": 2, "rule_criteria": {"rules": [{"label": "CPU Usage", "metric": + "cpu_usage", "unit": "percent", "aggregate_function": "avg", "operator": "gt", + "threshold": 90, "dimension_filters": []}]}, "alert_channels": [{"id": 10000, + "label": "Read-Write Channel", "url": "/monitor/alert-channels/10000", "type": + "alert-channels"}], "trigger_conditions": {"criteria_condition": "ALL", "polling_interval_seconds": 300, "evaluation_period_seconds": 300, "trigger_occurrences": 3}, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "created_by": "system", "updated_by": "system"}, {"id": 10004, "label": "High Disk Usage Plan Dedicated", @@ -77,15 +77,38 @@ interactions: 10005, "label": "High Disk Usage Plan Shared", "description": "Alert triggers when shared plan nodes experience sustained high disk usage, risking immediate resource exhaustion.", "service_type": "dbaas", "type": "system", "scope": "entity", - "class": "shared", "regions": [], "status": "enabled", "entity_ids": [], "has_more_resources": - false, "severity": 2, "rule_criteria": {"rules": [{"label": "Disk Space Usage", - "metric": "disk_usage", "unit": "percent", "aggregate_function": "avg", "operator": - "gt", "threshold": 90, "dimension_filters": []}]}, "alert_channels": [{"id": - 10000, "label": "Read-Write Channel", "url": "/monitor/alert-channels/10000", + "class": "shared", "regions": [], "status": "enabled", "entity_ids": ["389481", + "395222"], "has_more_resources": false, "severity": 2, "rule_criteria": {"rules": + [{"label": "Disk Space Usage", "metric": "disk_usage", "unit": "percent", "aggregate_function": + "avg", "operator": "gt", "threshold": 90, "dimension_filters": []}]}, "alert_channels": + [{"id": 10000, "label": "Read-Write Channel", "url": "/monitor/alert-channels/10000", "type": "alert-channels"}], "trigger_conditions": {"criteria_condition": "ALL", "polling_interval_seconds": 300, "evaluation_period_seconds": 300, "trigger_occurrences": 3}, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "created_by": - "system", "updated_by": "system"}]}' + "system", "updated_by": "system"}, {"id": 10681, "label": "tf-test-6724874609666216003", + "description": "An alert definition for ansible test", "service_type": "dbaas", + "type": "user", "scope": "entity", "class": null, "regions": [], "status": "disabled", + "entity_ids": [], "has_more_resources": false, "severity": 1, "rule_criteria": + {"rules": [{"label": "Memory Usage", "metric": "memory_usage", "unit": "percent", + "aggregate_function": "avg", "operator": "gt", "threshold": 90, "dimension_filters": + [{"label": "Node Type", "dimension_label": "node_type", "operator": "eq", "value": + "primary"}]}]}, "alert_channels": [{"id": 10000, "label": "Read-Write Channel", + "url": "/monitor/alert-channels/10000", "type": "alert-channels"}], "trigger_conditions": + {"criteria_condition": "ALL", "polling_interval_seconds": 300, "evaluation_period_seconds": + 300, "trigger_occurrences": 1}, "created": "2018-01-02T03:04:05", "updated": + "2018-01-02T03:04:05", "created_by": "ychen123", "updated_by": "ychen123"}, + {"id": 10684, "label": "tf-test-7004095832785037029", "description": "An alert + definition for ansible test", "service_type": "dbaas", "type": "user", "scope": + "entity", "class": null, "regions": [], "status": "disabled", "entity_ids": + [], "has_more_resources": false, "severity": 1, "rule_criteria": {"rules": [{"label": + "Memory Usage", "metric": "memory_usage", "unit": "percent", "aggregate_function": + "avg", "operator": "gt", "threshold": 90, "dimension_filters": [{"label": "Node + Type", "dimension_label": "node_type", "operator": "eq", "value": "primary"}]}]}, + "alert_channels": [{"id": 10000, "label": "Read-Write Channel", "url": "/monitor/alert-channels/10000", + "type": "alert-channels"}], "trigger_conditions": {"criteria_condition": "ALL", + "polling_interval_seconds": 300, "evaluation_period_seconds": 300, "trigger_occurrences": + 1}, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "created_by": + "ychen123", "updated_by": "ychen123"}]}' headers: Access-Control-Allow-Credentials: - "true" @@ -108,7 +131,7 @@ interactions: Content-Type: - application/json Expires: - - Mon, 22 Dec 2025 21:40:11 GMT + - Fri, 16 Jan 2026 16:01:00 GMT Pragma: - no-cache Strict-Transport-Security: @@ -147,7 +170,7 @@ interactions: url: https://api.linode.com/v4beta/monitor/services/dbaas/alert-definitions method: POST response: - body: '{"id": 10632, "label": "go-test-alert-definition-create", "description": + body: '{"id": 10726, "label": "go-test-alert-definition-create", "description": "Test alert definition creation", "service_type": "dbaas", "type": "user", "scope": "entity", "class": null, "regions": [], "status": "enabled", "entity_ids": [], "has_more_resources": false, "severity": 2, "rule_criteria": {"rules": [{"label": @@ -158,7 +181,7 @@ interactions: "type": "alert-channels"}], "trigger_conditions": {"criteria_condition": "ALL", "polling_interval_seconds": 300, "evaluation_period_seconds": 300, "trigger_occurrences": 1}, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "created_by": - "zliang27", "updated_by": "zliang27"}' + "ychen123", "updated_by": "ychen123"}' headers: Access-Control-Allow-Credentials: - "true" @@ -181,7 +204,7 @@ interactions: Content-Type: - application/json Expires: - - Mon, 22 Dec 2025 21:40:12 GMT + - Fri, 16 Jan 2026 16:01:01 GMT Pragma: - no-cache Strict-Transport-Security: @@ -216,10 +239,10 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/monitor/services/dbaas/alert-definitions/10632 + url: https://api.linode.com/v4beta/monitor/services/dbaas/alert-definitions/10726 method: PUT response: - body: '{"id": 10632, "label": "go-test-alert-definition-create-updated", "description": + body: '{"id": 10726, "label": "go-test-alert-definition-create-updated", "description": "Test alert definition creation", "service_type": "dbaas", "type": "user", "scope": "entity", "class": null, "regions": [], "status": "disabled", "entity_ids": [], "has_more_resources": false, "severity": 2, "rule_criteria": {"rules": [{"label": @@ -230,7 +253,7 @@ interactions: "type": "alert-channels"}], "trigger_conditions": {"criteria_condition": "ALL", "polling_interval_seconds": 300, "evaluation_period_seconds": 300, "trigger_occurrences": 1}, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "created_by": - "zliang27", "updated_by": "zliang27"}' + "ychen123", "updated_by": "ychen123"}' headers: Access-Control-Allow-Credentials: - "true" @@ -253,7 +276,7 @@ interactions: Content-Type: - application/json Expires: - - Mon, 22 Dec 2025 21:41:14 GMT + - Fri, 16 Jan 2026 16:02:03 GMT Pragma: - no-cache Strict-Transport-Security: @@ -287,7 +310,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/monitor/services/dbaas/alert-definitions/10632 + url: https://api.linode.com/v4beta/monitor/services/dbaas/alert-definitions/10726 method: DELETE response: body: '{}' @@ -315,7 +338,7 @@ interactions: Content-Type: - application/json Expires: - - Mon, 22 Dec 2025 21:41:15 GMT + - Fri, 16 Jan 2026 16:02:04 GMT Pragma: - no-cache Strict-Transport-Security: diff --git a/test/integration/fixtures/TestMonitorAlertDefinition_CreateWithIdempotency.yaml b/test/integration/fixtures/TestMonitorAlertDefinition_CreateWithIdempotency.yaml index 1408104f2..9e5486f19 100644 --- a/test/integration/fixtures/TestMonitorAlertDefinition_CreateWithIdempotency.yaml +++ b/test/integration/fixtures/TestMonitorAlertDefinition_CreateWithIdempotency.yaml @@ -2,117 +2,7 @@ version: 1 interactions: - request: - body: "" - form: {} - headers: - Accept: - - application/json - Content-Type: - - application/json - User-Agent: - - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/monitor/alert-channels?page=1 - method: GET - response: - body: '{"pages": 1, "page": 1, "results": 1, "data": [{"id": 10000, "label": "Read-Write - Channel", "channel_type": "email", "type": "system", "content": {"email": {"email_addresses": - ["Users-with-read-write-access-to-resources"]}}, "alerts": [{"id": 10000, "label": - "High Memory Usage Plan Dedicated", "url": "/monitor/alerts-definitions/10000", - "type": "alerts-definitions"}, {"id": 10001, "label": "High Memory Usage Plan - Shared", "url": "/monitor/alerts-definitions/10001", "type": "alerts-definitions"}, - {"id": 10002, "label": "High CPU Usage Plan Dedicated", "url": "/monitor/alerts-definitions/10002", - "type": "alerts-definitions"}, {"id": 10003, "label": "High CPU Usage Plan Shared", - "url": "/monitor/alerts-definitions/10003", "type": "alerts-definitions"}, {"id": - 10004, "label": "High Disk Usage Plan Dedicated", "url": "/monitor/alerts-definitions/10004", - "type": "alerts-definitions"}, {"id": 10005, "label": "High Disk Usage Plan - Shared", "url": "/monitor/alerts-definitions/10005", "type": "alerts-definitions"}, - {"id": 10470, "label": "jgliahjs_updated", "url": "/monitor/alerts-definitions/10470", - "type": "alerts-definitions"}, {"id": 10503, "label": "obohragp-e2e-alert-1763051867-updated", - "url": "/monitor/alerts-definitions/10503", "type": "alerts-definitions"}, {"id": - 10504, "label": "lczvmpvu-e2e-alert-1763053808", "url": "/monitor/alerts-definitions/10504", - "type": "alerts-definitions"}, {"id": 10505, "label": "ocachcjp-e2e-alert-1763055498-updated", - "url": "/monitor/alerts-definitions/10505", "type": "alerts-definitions"}, {"id": - 10506, "label": "lrbbifob-e2e-alert-1763057283", "url": "/monitor/alerts-definitions/10506", - "type": "alerts-definitions"}, {"id": 10507, "label": "cbbrhfnv-e2e-alert-1763057415", - "url": "/monitor/alerts-definitions/10507", "type": "alerts-definitions"}, {"id": - 10508, "label": "gqzzxdgh-e2e-alert-1763057687", "url": "/monitor/alerts-definitions/10508", - "type": "alerts-definitions"}, {"id": 10509, "label": "smqsmhst-e2e-alert-1763058424", - "url": "/monitor/alerts-definitions/10509", "type": "alerts-definitions"}, {"id": - 10510, "label": "vygdkmkw-e2e-alert-1763058509", "url": "/monitor/alerts-definitions/10510", - "type": "alerts-definitions"}, {"id": 10511, "label": "dvlgyyvr-e2e-alert-1763058560", - "url": "/monitor/alerts-definitions/10511", "type": "alerts-definitions"}, {"id": - 10512, "label": "pscihptu-e2e-alert-1763058653-updated", "url": "/monitor/alerts-definitions/10512", - "type": "alerts-definitions"}, {"id": 10513, "label": "emxhsdlm-e2e-alert-1763059164-updated", - "url": "/monitor/alerts-definitions/10513", "type": "alerts-definitions"}, {"id": - 10514, "label": "fxbekqdl-e2e-alert-1763059845-updated", "url": "/monitor/alerts-definitions/10514", - "type": "alerts-definitions"}, {"id": 10529, "label": "Test Database Standby - Host", "url": "/monitor/alerts-definitions/10529", "type": "alerts-definitions"}, - {"id": 10623, "label": "go-test-alert-definition-idempotency-1766438857602066000", - "url": "/monitor/alerts-definitions/10623", "type": "alerts-definitions"}, {"id": - 10624, "label": "go-test-alert-definition-create-updated", "url": "/monitor/alerts-definitions/10624", - "type": "alerts-definitions"}, {"id": 10625, "label": "go-test-alert-definition-idempotency-1766439008803464000", - "url": "/monitor/alerts-definitions/10625", "type": "alerts-definitions"}, {"id": - 10626, "label": "go-test-alert-definition-create-updated", "url": "/monitor/alerts-definitions/10626", - "type": "alerts-definitions"}, {"id": 10627, "label": "go-test-alert-definition-idempotency-1766439204872603000", - "url": "/monitor/alerts-definitions/10627", "type": "alerts-definitions"}, {"id": - 10628, "label": "go-test-alert-definition-create-updated", "url": "/monitor/alerts-definitions/10628", - "type": "alerts-definitions"}, {"id": 10629, "label": "go-test-alert-definition-idempotency-1766439362488609000", - "url": "/monitor/alerts-definitions/10629", "type": "alerts-definitions"}, {"id": - 10630, "label": "go-test-alert-definition-create-updated", "url": "/monitor/alerts-definitions/10630", - "type": "alerts-definitions"}, {"id": 10631, "label": "go-test-alert-definition-idempotency-1766439501203284000", - "url": "/monitor/alerts-definitions/10631", "type": "alerts-definitions"}, {"id": - 10632, "label": "go-test-alert-definition-create-updated", "url": "/monitor/alerts-definitions/10632", - "type": "alerts-definitions"}], "created": "2018-01-02T03:04:05", "updated": - "2018-01-02T03:04:05", "created_by": "system", "updated_by": "system"}]}' - headers: - Access-Control-Allow-Credentials: - - "true" - Access-Control-Allow-Headers: - - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter - Access-Control-Allow-Methods: - - HEAD, GET, OPTIONS, POST, PUT, DELETE - Access-Control-Allow-Origin: - - '*' - Access-Control-Expose-Headers: - - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status - Akamai-Internal-Account: - - '*' - Cache-Control: - - max-age=0, no-cache, no-store - Connection: - - keep-alive - Content-Security-Policy: - - default-src 'none' - Content-Type: - - application/json - Expires: - - Mon, 22 Dec 2025 21:41:18 GMT - Pragma: - - no-cache - Strict-Transport-Security: - - max-age=31536000 - Vary: - - Authorization, X-Filter - - Authorization, X-Filter - - Accept-Encoding - X-Accepted-Oauth-Scopes: - - monitor:read_only - X-Content-Type-Options: - - nosniff - X-Frame-Options: - - DENY - - DENY - X-Oauth-Scopes: - - '*' - X-Ratelimit-Limit: - - "1840" - X-Xss-Protection: - - 1; mode=block - status: 200 OK - code: 200 - duration: "" -- request: - body: '{"label":"go-test-alert-definition-idempotency-1766439678890029000","severity":2,"channel_ids":[10000],"rule_criteria":{"rules":[{"aggregate_function":"avg","dimension_filters":[{"dimension_label":"node_type","operator":"eq","value":"primary"}],"metric":"memory_usage","operator":"gt","threshold":90}]},"trigger_conditions":{"criteria_condition":"ALL","evaluation_period_seconds":300,"polling_interval_seconds":300,"trigger_occurrences":1},"description":"Test + body: '{"label":"go-test-alert-definition-idempotency-1768579458757163000","severity":2,"channel_ids":[10000],"rule_criteria":{"rules":[{"aggregate_function":"avg","dimension_filters":[{"dimension_label":"node_type","operator":"eq","value":"primary"}],"metric":"memory_usage","operator":"gt","threshold":90}]},"trigger_conditions":{"criteria_condition":"ALL","evaluation_period_seconds":300,"polling_interval_seconds":300,"trigger_occurrences":1},"description":"Test alert definition creation with idempotency"}' form: {} headers: @@ -125,7 +15,7 @@ interactions: url: https://api.linode.com/v4beta/monitor/services/dbaas/alert-definitions method: POST response: - body: '{"id": 10633, "label": "go-test-alert-definition-idempotency-1766439678890029000", + body: '{"id": 10727, "label": "go-test-alert-definition-idempotency-1768579458757163000", "description": "Test alert definition creation with idempotency", "service_type": "dbaas", "type": "user", "scope": "entity", "class": null, "regions": [], "status": "enabled", "entity_ids": [], "has_more_resources": false, "severity": 2, "rule_criteria": @@ -136,7 +26,7 @@ interactions: "url": "/monitor/alert-channels/10000", "type": "alert-channels"}], "trigger_conditions": {"criteria_condition": "ALL", "polling_interval_seconds": 300, "evaluation_period_seconds": 300, "trigger_occurrences": 1}, "created": "2018-01-02T03:04:05", "updated": - "2018-01-02T03:04:05", "created_by": "zliang27", "updated_by": "zliang27"}' + "2018-01-02T03:04:05", "created_by": "ychen123", "updated_by": "ychen123"}' headers: Access-Control-Allow-Credentials: - "true" @@ -159,7 +49,7 @@ interactions: Content-Type: - application/json Expires: - - Mon, 22 Dec 2025 21:41:19 GMT + - Fri, 16 Jan 2026 16:04:19 GMT Pragma: - no-cache Strict-Transport-Security: @@ -184,7 +74,7 @@ interactions: code: 200 duration: "" - request: - body: '{"label":"go-test-alert-definition-idempotency-1766439678890029000","severity":2,"channel_ids":[10000],"rule_criteria":{"rules":[{"aggregate_function":"avg","dimension_filters":[{"dimension_label":"node_type","operator":"eq","value":"primary"}],"metric":"memory_usage","operator":"gt","threshold":90}]},"trigger_conditions":{"criteria_condition":"ALL","evaluation_period_seconds":300,"polling_interval_seconds":300,"trigger_occurrences":1},"description":"Test + body: '{"label":"go-test-alert-definition-idempotency-1768579458757163000","severity":2,"channel_ids":[10000],"rule_criteria":{"rules":[{"aggregate_function":"avg","dimension_filters":[{"dimension_label":"node_type","operator":"eq","value":"primary"}],"metric":"memory_usage","operator":"gt","threshold":90}]},"trigger_conditions":{"criteria_condition":"ALL","evaluation_period_seconds":300,"polling_interval_seconds":300,"trigger_occurrences":1},"description":"Test alert definition creation with idempotency"}' form: {} headers: @@ -215,7 +105,7 @@ interactions: Content-Type: - application/json Expires: - - Mon, 22 Dec 2025 21:41:20 GMT + - Fri, 16 Jan 2026 16:04:20 GMT Pragma: - no-cache Strict-Transport-Security: @@ -241,7 +131,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/monitor/services/dbaas/alert-definitions/10633 + url: https://api.linode.com/v4beta/monitor/services/dbaas/alert-definitions/10727 method: DELETE response: body: '{}' @@ -269,7 +159,7 @@ interactions: Content-Type: - application/json Expires: - - Mon, 22 Dec 2025 21:41:22 GMT + - Fri, 16 Jan 2026 16:04:21 GMT Pragma: - no-cache Strict-Transport-Security: diff --git a/test/integration/monitor_alert_definitions_test.go b/test/integration/monitor_alert_definitions_test.go index 90ded2122..025b745be 100644 --- a/test/integration/monitor_alert_definitions_test.go +++ b/test/integration/monitor_alert_definitions_test.go @@ -12,6 +12,9 @@ import ( const ( testMonitorAlertDefinitionServiceType = "dbaas" + + // TODO: use a fixed channel id for now until the alert channel has been fixed. + channelID = 10000 ) func TestMonitorAlertDefinition_smoke(t *testing.T) { @@ -44,27 +47,28 @@ func TestMonitorAlertDefinition_smoke(t *testing.T) { // Basic assertions based on the fixture assert.NoError(t, err) - // Determine a channel ID to use for creating a new alert definition: - var channelID int - var fetchedChannelLabel string - var fetchedChannelID int - if len(alerts) > 0 && len(alerts[0].AlertChannels) > 0 { - channelID = alerts[0].AlertChannels[0].ID - fetchedChannelID = alerts[0].AlertChannels[0].ID - fetchedChannelLabel = alerts[0].AlertChannels[0].Label - } else { - // Fallback to ListAlertChannels to get available channels - channels, err := client.ListAlertChannels(context.Background(), nil) - if err != nil || len(channels) == 0 { - t.Fatalf("failed to determine a monitor channel to use: %s", err) - } - channelID = channels[0].ID - fetchedChannelID = channels[0].ID - fetchedChannelLabel = channels[0].Label - } - // Validate the chosen channel - assert.NotZero(t, fetchedChannelID, "fetchedChannel.ID should not be zero") - assert.NotEmpty(t, fetchedChannelLabel, "fetchedChannel.Label should not be empty") + // TODO: Use a fixed channel id for now until the alert channel has been fixed. + //// Determine a channel ID to use for creating a new alert definition: + //var channelID int + //var fetchedChannelLabel string + //var fetchedChannelID int + //if len(alerts) > 0 && len(alerts[0].AlertChannels) > 0 { + // channelID = alerts[0].AlertChannels[0].ID + // fetchedChannelID = alerts[0].AlertChannels[0].ID + // fetchedChannelLabel = alerts[0].AlertChannels[0].Label + //} else { + // // Fallback to ListAlertChannels to get available channels + // channels, err := client.ListAlertChannels(context.Background(), nil) + // if err != nil || len(channels) == 0 { + // t.Fatalf("failed to determine a monitor channel to use: %s", err) + // } + // channelID = channels[0].ID + // fetchedChannelID = channels[0].ID + // fetchedChannelLabel = channels[0].Label + //} + //// Validate the chosen channel + //assert.NotZero(t, fetchedChannelID, "fetchedChannel.ID should not be zero") + //assert.NotEmpty(t, fetchedChannelLabel, "fetchedChannel.Label should not be empty") // Test creating a new Monitor Alert Definition createOpts := linodego.AlertDefinitionCreateOptions{ @@ -203,32 +207,34 @@ func TestMonitorAlertDefinitions_List(t *testing.T) { } } -func TestMonitorAlertChannels_List(t *testing.T) { - client, teardown := createTestClient(t, "fixtures/TestMonitorAlertChannels_List") - defer teardown() - - // List all alert channels - channels, err := client.ListAlertChannels(context.Background(), nil) - assert.NoError(t, err) - assert.NotEmpty(t, channels, "Expected at least one alert channel") - - for _, channel := range channels { - assert.NotZero(t, channel.ID) - assert.NotEmpty(t, channel.Label) - assert.NotEmpty(t, channel.ChannelType) - } -} +// TODO: Disable this test until we can query alert channels correctly. +//func TestMonitorAlertChannels_List(t *testing.T) { +// client, teardown := createTestClient(t, "fixtures/TestMonitorAlertChannels_List") +// defer teardown() +// +// // List all alert channels +// channels, err := client.ListAlertChannels(context.Background(), nil) +// assert.NoError(t, err) +// assert.NotEmpty(t, channels, "Expected at least one alert channel") +// +// for _, channel := range channels { +// assert.NotZero(t, channel.ID) +// assert.NotEmpty(t, channel.Label) +// assert.NotEmpty(t, channel.ChannelType) +// } +//} func TestMonitorAlertDefinition_CreateWithIdempotency(t *testing.T) { client, teardown := createTestClient(t, "fixtures/TestMonitorAlertDefinition_CreateWithIdempotency") defer teardown() - // Get a channel ID to use - channels, err := client.ListAlertChannels(context.Background(), nil) - if err != nil || len(channels) == 0 { - t.Fatalf("failed to determine a monitor channel to use: %s", err) - } - channelID := channels[0].ID + // TODO: use a fixed channel id for now until the alert channel has been fixed. + //// Get a channel ID to use + //channels, err := client.ListAlertChannels(context.Background(), nil) + //if err != nil || len(channels) == 0 { + // t.Fatalf("failed to determine a monitor channel to use: %s", err) + //} + //channelID := channels[0].ID uniqueLabel := fmt.Sprintf("go-test-alert-definition-idempotency-%d", time.Now().UnixNano()) diff --git a/waitfor.go b/waitfor.go index 3a001e619..afa998a70 100644 --- a/waitfor.go +++ b/waitfor.go @@ -849,3 +849,33 @@ func eventMatchesSecondary(configuredID any, e Event) bool { return secondaryID == configuredID } + +// WaitForAlertDefinitionStatusReady waits for the Alert Definition to reach the ready status (not in progress) +func (client Client) WaitForAlertDefinitionStatusReady( + ctx context.Context, + serviceType string, + alertID int, + timeoutSeconds int, +) (*AlertDefinition, error) { + ctx, cancel := context.WithTimeout(ctx, time.Duration(timeoutSeconds)*time.Second) + defer cancel() + + ticker := time.NewTicker(client.pollInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + alertDef, err := client.GetMonitorAlertDefinition(ctx, serviceType, alertID) + if err != nil { + return alertDef, err + } + + if alertDef.Status != AlertDefinitionStatusInProgress { + return alertDef, nil + } + case <-ctx.Done(): + return nil, fmt.Errorf("failed to wait for AlertDefinition %d status ready: %w", alertID, ctx.Err()) + } + } +}