Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,13 @@ type InstanceSpec struct {

// InstanceAlert represents a metric alert
type InstanceAlert struct {
CPU int `json:"cpu"`
IO int `json:"io"`
NetworkIn int `json:"network_in"`
NetworkOut int `json:"network_out"`
TransferQuota int `json:"transfer_quota"`
CPU int `json:"cpu"`
IO int `json:"io"`
NetworkIn int `json:"network_in"`
NetworkOut int `json:"network_out"`
TransferQuota int `json:"transfer_quota"`
SystemAlerts *[]int `json:"system_alerts,omitempty"`
UserAlerts *[]int `json:"user_alerts,omitempty"`
}

// InstanceBackup represents backup settings for an instance
Expand Down Expand Up @@ -228,6 +230,15 @@ type InstanceCreateOptions struct {

// NOTE: MaintenancePolicy can only be used with v4beta.
MaintenancePolicy *string `json:"maintenance_policy,omitempty"`

// Note: Alerts can only be used with v4beta.
Alerts *InstanceACLPAlertsOptions `json:"alerts,omitempty"`
}

// InstanceACLPAlertsOptions represents ACLP alerts options for instance creation and cloning.
Comment on lines +235 to +238
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The InstanceCreateOptions.Alerts field uses InstanceACLPAlertsOptions, but InstanceUpdateOptions.Alerts (line 226 in the test file) uses InstanceAlert. This inconsistency creates confusion about which type should be used for updates versus creation. Consider using the same type for both operations or clearly documenting why different types are needed.

Suggested change
Alerts *InstanceACLPAlertsOptions `json:"alerts,omitempty"`
}
// InstanceACLPAlertsOptions represents ACLP alerts options for instance creation and cloning.
// Note: This field intentionally uses InstanceACLPAlertsOptions, which differs
// from the alerts type used in InstanceUpdateOptions (InstanceAlert). The API
// for configuring alerts at creation/cloning time is distinct from the API
// used when updating an existing Instance's alerts.
Alerts *InstanceACLPAlertsOptions `json:"alerts,omitempty"`
}
// InstanceACLPAlertsOptions represents ACLP alerts options for instance creation
// and cloning. This type is distinct from the alerts type used on
// InstanceUpdateOptions (InstanceAlert) to reflect differences in the API for
// initial (create/clone) configuration versus subsequent updates.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is API inconsistency in creating and updating request.

type InstanceACLPAlertsOptions struct {
SystemAlerts *[]int `json:"system_alerts,omitempty"`
UserAlerts *[]int `json:"user_alerts,omitempty"`
}

// InstanceCreatePlacementGroupOptions represents the placement group
Expand Down Expand Up @@ -403,6 +414,9 @@ type InstanceCloneOptions struct {

// Deprecated: group is a deprecated property denoting a group label for the Linode.
Group string `json:"group,omitempty"`

// Note: Alerts can only be used with v4beta.
Alerts *InstanceACLPAlertsOptions `json:"alerts,omitempty"`
}

// InstanceResizeOptions is an options struct used when resizing an instance
Expand Down
11 changes: 10 additions & 1 deletion test/unit/fixtures/instance_clone.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,14 @@
"region": "us-east",
"type": "g6-standard-1",
"status": "provisioning",
"maintenance_policy": "linode/migrate"
"maintenance_policy": "linode/migrate",
"alerts": {
"cpu": 0,
"io": 0,
"network_in": 0,
"network_out": 0,
"transfer_quota": 0,
"system_alerts": [123,456],
"user_alerts": [555]
}
}
11 changes: 10 additions & 1 deletion test/unit/fixtures/instance_create.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,14 @@
"region": "us-east",
"type": "g6-standard-1",
"status": "provisioning",
"maintenance_policy": "linode/migrate"
"maintenance_policy": "linode/migrate",
"alerts": {
"cpu": 0,
"io": 0,
"network_in": 0,
"network_out": 0,
"transfer_quota": 0,
"system_alerts": [123,456],
"user_alerts": [555]
}
}
11 changes: 10 additions & 1 deletion test/unit/fixtures/instance_get.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,14 @@
"placement_group": {
"migrating_to": 2468
},
"maintenance_policy": "linode/migrate"
"maintenance_policy": "linode/migrate",
"alerts": {
"cpu": 0,
"io": 0,
"network_in": 0,
"network_out": 0,
"transfer_quota": 0,
"system_alerts": [123,456],
"user_alerts": [555]
}
}
11 changes: 10 additions & 1 deletion test/unit/fixtures/instance_update.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{
"id": 123,
"label": "updated-instance",
"maintenance_policy": "linode/power_off_on"
"maintenance_policy": "linode/power_off_on",
"alerts": {
"cpu": 0,
"io": 0,
"network_in": 0,
"network_out": 0,
"transfer_quota": 0,
"system_alerts": [123,456],
"user_alerts": [555]
}
}
9 changes: 9 additions & 0 deletions test/unit/fixtures/interface_list.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
"vlan": {
"vlan_label": "my_vlan",
"ipam_address": "10.0.0.1/24"
},
"alerts": {
"cpu": 0,
"io": 0,
"network_in": 0,
"network_out": 0,
"transfer_quota": 0,
"system_alerts": [123,456],
"user_alerts": [555]
}
},
{
Expand Down
77 changes: 77 additions & 0 deletions test/unit/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ func TestInstances_List(t *testing.T) {
require.NotNil(t, linode.PlacementGroup.MigratingTo)
assert.Equal(t, 2468, *linode.PlacementGroup.MigratingTo)
assert.Equal(t, "linode/migrate", linode.MaintenancePolicy)
if linode.Alerts.SystemAlerts != nil {
systemAlerts := *linode.Alerts.SystemAlerts
if len(systemAlerts) > 2 {
assert.Equal(t, 123, systemAlerts[0])
assert.Equal(t, 456, systemAlerts[1])
}
}
if linode.Alerts.UserAlerts != nil {
userAlerts := *linode.Alerts.UserAlerts
if len(userAlerts) > 0 {
assert.Equal(t, 555, userAlerts[0])
}
}
}

func TestInstance_Get(t *testing.T) {
Expand Down Expand Up @@ -77,6 +90,19 @@ func TestInstance_Get(t *testing.T) {
assert.Equal(t, "linode/migrate", instance.MaintenancePolicy)
require.NotNil(t, instance.PlacementGroup.MigratingTo)
assert.Equal(t, 2468, *instance.PlacementGroup.MigratingTo)
if instance.Alerts.SystemAlerts != nil {
systemAlerts := *instance.Alerts.SystemAlerts
if len(systemAlerts) > 2 {
assert.Equal(t, 123, systemAlerts[0])
assert.Equal(t, 456, systemAlerts[1])
}
}
if instance.Alerts.UserAlerts != nil {
userAlerts := *instance.Alerts.UserAlerts
if len(userAlerts) > 0 {
assert.Equal(t, 555, userAlerts[0])
}
}
}

func TestInstance_Migrate(t *testing.T) {
Expand Down Expand Up @@ -180,6 +206,10 @@ func TestInstance_Create(t *testing.T) {
Image: "linode/ubuntu22.04",
RootPass: "securepassword",
MaintenancePolicy: linodego.Pointer("linode/migrate"),
Alerts: &linodego.InstanceACLPAlertsOptions{
SystemAlerts: &[]int{123, 456},
UserAlerts: &[]int{555},
},
}

base.MockPost("linode/instances", fixtureData)
Expand All @@ -188,6 +218,19 @@ func TestInstance_Create(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "new-instance", instance.Label)
assert.Equal(t, "linode/migrate", instance.MaintenancePolicy)
if instance.Alerts.SystemAlerts != nil {
systemAlerts := *instance.Alerts.SystemAlerts
if len(systemAlerts) > 2 {
assert.Equal(t, 123, systemAlerts[0])
assert.Equal(t, 456, systemAlerts[1])
}
}
if instance.Alerts.UserAlerts != nil {
userAlerts := *instance.Alerts.UserAlerts
if len(userAlerts) > 0 {
assert.Equal(t, 555, userAlerts[0])
}
}
}

func TestInstance_Update(t *testing.T) {
Expand All @@ -201,6 +244,10 @@ func TestInstance_Update(t *testing.T) {
updateOptions := linodego.InstanceUpdateOptions{
Label: "updated-instance",
MaintenancePolicy: linodego.Pointer("linode/power_off_on"),
Alerts: &linodego.InstanceAlert{
SystemAlerts: &[]int{123, 456},
UserAlerts: &[]int{555},
},
}

base.MockPut("linode/instances/123", fixtureData)
Expand All @@ -209,6 +256,19 @@ func TestInstance_Update(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "updated-instance", instance.Label)
assert.Equal(t, "linode/power_off_on", instance.MaintenancePolicy)
if instance.Alerts.SystemAlerts != nil {
systemAlerts := *instance.Alerts.SystemAlerts
if len(systemAlerts) > 2 {
assert.Equal(t, 123, systemAlerts[0])
assert.Equal(t, 456, systemAlerts[1])
}
}
if instance.Alerts.UserAlerts != nil {
userAlerts := *instance.Alerts.UserAlerts
if len(userAlerts) > 0 {
assert.Equal(t, 555, userAlerts[0])
}
}
}

func TestInstance_Delete(t *testing.T) {
Expand Down Expand Up @@ -256,6 +316,10 @@ func TestInstance_Clone(t *testing.T) {
Region: "us-east",
Type: "g6-standard-1",
Label: "cloned-instance",
Alerts: &linodego.InstanceACLPAlertsOptions{
SystemAlerts: &[]int{123, 456},
UserAlerts: &[]int{555},
},
}

base.MockPost("linode/instances/123/clone", fixtureData)
Expand All @@ -264,6 +328,19 @@ func TestInstance_Clone(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "cloned-instance", instance.Label)
assert.Equal(t, "linode/migrate", instance.MaintenancePolicy)
if instance.Alerts.SystemAlerts != nil {
systemAlerts := *instance.Alerts.SystemAlerts
if len(systemAlerts) > 2 {
assert.Equal(t, 123, systemAlerts[0])
assert.Equal(t, 456, systemAlerts[1])
}
}
if instance.Alerts.UserAlerts != nil {
userAlerts := *instance.Alerts.UserAlerts
if len(userAlerts) > 0 {
assert.Equal(t, 555, userAlerts[0])
}
}
}

func TestInstance_Resize(t *testing.T) {
Expand Down