From 50aa9cde32eb62ac1fecc4aa6fd646424dea8d54 Mon Sep 17 00:00:00 2001 From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com> Date: Fri, 1 Sep 2023 10:18:37 -0400 Subject: [PATCH 1/3] new: Implement region-specific pricing (#367) * adding missing events (#365) * Implement region-specific pricing * Add test * Revert go.work.sum * nosec --------- Co-authored-by: Jacob Riddle <87780794+jriddle-linode@users.noreply.github.com> --- account_events.go | 221 +++++++++++++++++++-------------- account_invoices.go | 2 + test/integration/types_test.go | 44 +++++++ types.go | 36 ++++-- 4 files changed, 195 insertions(+), 108 deletions(-) diff --git a/account_events.go b/account_events.go index 137324486..c6dc005e5 100644 --- a/account_events.go +++ b/account_events.go @@ -55,101 +55,132 @@ type EventAction string // EventAction constants represent the actions that cause an Event. New actions may be added in the future. const ( - ActionAccountUpdate EventAction = "account_update" - ActionAccountSettingsUpdate EventAction = "account_settings_update" - ActionBackupsEnable EventAction = "backups_enable" - ActionBackupsCancel EventAction = "backups_cancel" - ActionBackupsRestore EventAction = "backups_restore" - ActionCommunityQuestionReply EventAction = "community_question_reply" - ActionCommunityLike EventAction = "community_like" - ActionCreateCardUpdated EventAction = "credit_card_updated" - ActionDatabaseCreate EventAction = "database_create" - ActionDatabaseDegraded EventAction = "database_degraded" - ActionDatabaseDelete EventAction = "database_delete" - ActionDatabaseFailed EventAction = "database_failed" - ActionDatabaseUpdate EventAction = "database_update" - ActionDatabaseCreateFailed EventAction = "database_create_failed" - ActionDatabaseUpdateFailed EventAction = "database_update_failed" - ActionDatabaseBackupCreate EventAction = "database_backup_create" - ActionDatabaseBackupRestore EventAction = "database_backup_restore" - ActionDatabaseCredentialsReset EventAction = "database_credentials_reset" - ActionDiskCreate EventAction = "disk_create" - ActionDiskDelete EventAction = "disk_delete" - ActionDiskUpdate EventAction = "disk_update" - ActionDiskDuplicate EventAction = "disk_duplicate" - ActionDiskImagize EventAction = "disk_imagize" - ActionDiskResize EventAction = "disk_resize" - ActionDNSRecordCreate EventAction = "dns_record_create" - ActionDNSRecordDelete EventAction = "dns_record_delete" - ActionDNSRecordUpdate EventAction = "dns_record_update" - ActionDNSZoneCreate EventAction = "dns_zone_create" - ActionDNSZoneDelete EventAction = "dns_zone_delete" - ActionDNSZoneUpdate EventAction = "dns_zone_update" - ActionFirewallCreate EventAction = "firewall_create" - ActionFirewallDelete EventAction = "firewall_delete" - ActionFirewallDisable EventAction = "firewall_disable" - ActionFirewallEnable EventAction = "firewall_enable" - ActionFirewallUpdate EventAction = "firewall_update" - ActionFirewallDeviceAdd EventAction = "firewall_device_add" - ActionFirewallDeviceRemove EventAction = "firewall_device_remove" - ActionHostReboot EventAction = "host_reboot" - ActionImageDelete EventAction = "image_delete" - ActionImageUpdate EventAction = "image_update" - ActionImageUpload EventAction = "image_upload" - ActionLassieReboot EventAction = "lassie_reboot" - ActionLinodeAddIP EventAction = "linode_addip" - ActionLinodeBoot EventAction = "linode_boot" - ActionLinodeClone EventAction = "linode_clone" - ActionLinodeCreate EventAction = "linode_create" - ActionLinodeDelete EventAction = "linode_delete" - ActionLinodeUpdate EventAction = "linode_update" - ActionLinodeDeleteIP EventAction = "linode_deleteip" - ActionLinodeMigrate EventAction = "linode_migrate" - ActionLinodeMutate EventAction = "linode_mutate" - ActionLinodeMutateCreate EventAction = "linode_mutate_create" - ActionLinodeReboot EventAction = "linode_reboot" - ActionLinodeRebuild EventAction = "linode_rebuild" - ActionLinodeResize EventAction = "linode_resize" - ActionLinodeResizeCreate EventAction = "linode_resize_create" - ActionLinodeShutdown EventAction = "linode_shutdown" - ActionLinodeSnapshot EventAction = "linode_snapshot" - ActionLinodeConfigCreate EventAction = "linode_config_create" - ActionLinodeConfigDelete EventAction = "linode_config_delete" - ActionLinodeConfigUpdate EventAction = "linode_config_update" - ActionLishBoot EventAction = "lish_boot" - ActionLKENodeCreate EventAction = "lke_node_create" - ActionLongviewClientCreate EventAction = "longviewclient_create" - ActionLongviewClientDelete EventAction = "longviewclient_delete" - ActionLongviewClientUpdate EventAction = "longviewclient_update" - ActionManagedDisabled EventAction = "managed_disabled" - ActionManagedEnabled EventAction = "managed_enabled" - ActionManagedServiceCreate EventAction = "managed_service_create" - ActionManagedServiceDelete EventAction = "managed_service_delete" - ActionNodebalancerCreate EventAction = "nodebalancer_create" - ActionNodebalancerDelete EventAction = "nodebalancer_delete" - ActionNodebalancerUpdate EventAction = "nodebalancer_update" - ActionNodebalancerConfigCreate EventAction = "nodebalancer_config_create" - ActionNodebalancerConfigDelete EventAction = "nodebalancer_config_delete" - ActionNodebalancerConfigUpdate EventAction = "nodebalancer_config_update" - ActionPasswordReset EventAction = "password_reset" - ActionPaymentSubmitted EventAction = "payment_submitted" - ActionStackScriptCreate EventAction = "stackscript_create" - ActionStackScriptDelete EventAction = "stackscript_delete" - ActionStackScriptUpdate EventAction = "stackscript_update" - ActionStackScriptPublicize EventAction = "stackscript_publicize" - ActionStackScriptRevise EventAction = "stackscript_revise" - ActionTFADisabled EventAction = "tfa_disabled" - ActionTFAEnabled EventAction = "tfa_enabled" - ActionTicketAttachmentUpload EventAction = "ticket_attachment_upload" - ActionTicketCreate EventAction = "ticket_create" - ActionTicketUpdate EventAction = "ticket_update" - ActionVolumeAttach EventAction = "volume_attach" - ActionVolumeClone EventAction = "volume_clone" - ActionVolumeCreate EventAction = "volume_create" - ActionVolumeDelte EventAction = "volume_delete" - ActionVolumeUpdate EventAction = "volume_update" - ActionVolumeDetach EventAction = "volume_detach" - ActionVolumeResize EventAction = "volume_resize" + ActionAccountUpdate EventAction = "account_update" + ActionAccountSettingsUpdate EventAction = "account_settings_update" + ActionBackupsEnable EventAction = "backups_enable" + ActionBackupsCancel EventAction = "backups_cancel" + ActionBackupsRestore EventAction = "backups_restore" + ActionCommunityQuestionReply EventAction = "community_question_reply" + ActionCommunityLike EventAction = "community_like" + ActionCreateCardUpdated EventAction = "credit_card_updated" + ActionDatabaseCreate EventAction = "database_create" + ActionDatabaseDegraded EventAction = "database_degraded" + ActionDatabaseDelete EventAction = "database_delete" + ActionDatabaseFailed EventAction = "database_failed" + ActionDatabaseUpdate EventAction = "database_update" + ActionDatabaseCreateFailed EventAction = "database_create_failed" + ActionDatabaseUpdateFailed EventAction = "database_update_failed" + ActionDatabaseBackupCreate EventAction = "database_backup_create" + ActionDatabaseBackupRestore EventAction = "database_backup_restore" + ActionDatabaseCredentialsReset EventAction = "database_credentials_reset" + ActionDiskCreate EventAction = "disk_create" + ActionDiskDelete EventAction = "disk_delete" + ActionDiskUpdate EventAction = "disk_update" + ActionDiskDuplicate EventAction = "disk_duplicate" + ActionDiskImagize EventAction = "disk_imagize" + ActionDiskResize EventAction = "disk_resize" + ActionDNSRecordCreate EventAction = "dns_record_create" + ActionDNSRecordDelete EventAction = "dns_record_delete" + ActionDNSRecordUpdate EventAction = "dns_record_update" + ActionDNSZoneCreate EventAction = "dns_zone_create" + ActionDNSZoneDelete EventAction = "dns_zone_delete" + ActionDNSZoneUpdate EventAction = "dns_zone_update" + ActionDNSZoneImport EventAction = "dns_zone_import" + ActionEntityTransferAccept EventAction = "entity_transfer_accept" + ActionEntityTransferCancel EventAction = "entity_transfer_cancel" + ActionEntityTransferCreate EventAction = "entity_transfer_create" + ActionEntityTransferFail EventAction = "entity_transfer_fail" + ActionEntityTransferStale EventAction = "entity_transfer_stale" + ActionFirewallCreate EventAction = "firewall_create" + ActionFirewallDelete EventAction = "firewall_delete" + ActionFirewallDisable EventAction = "firewall_disable" + ActionFirewallEnable EventAction = "firewall_enable" + ActionFirewallUpdate EventAction = "firewall_update" + ActionFirewallDeviceAdd EventAction = "firewall_device_add" + ActionFirewallDeviceRemove EventAction = "firewall_device_remove" + ActionHostReboot EventAction = "host_reboot" + ActionImageDelete EventAction = "image_delete" + ActionImageUpdate EventAction = "image_update" + ActionImageUpload EventAction = "image_upload" + ActionIPAddressUpdate EventAction = "ipaddress_update" + ActionLassieReboot EventAction = "lassie_reboot" + ActionLinodeAddIP EventAction = "linode_addip" + ActionLinodeBoot EventAction = "linode_boot" + ActionLinodeClone EventAction = "linode_clone" + ActionLinodeCreate EventAction = "linode_create" + ActionLinodeDelete EventAction = "linode_delete" + ActionLinodeUpdate EventAction = "linode_update" + ActionLinodeDeleteIP EventAction = "linode_deleteip" + ActionLinodeMigrate EventAction = "linode_migrate" + ActionLinodeMigrateDatacenter EventAction = "linode_migrate_datacenter" + ActionLinodeMigrateDatacenterCreate EventAction = "linode_migrate_datacenter_create" + ActionLinodeMutate EventAction = "linode_mutate" + ActionLinodeMutateCreate EventAction = "linode_mutate_create" + ActionLinodeReboot EventAction = "linode_reboot" + ActionLinodeRebuild EventAction = "linode_rebuild" + ActionLinodeResize EventAction = "linode_resize" + ActionLinodeResizeCreate EventAction = "linode_resize_create" + ActionLinodeShutdown EventAction = "linode_shutdown" + ActionLinodeSnapshot EventAction = "linode_snapshot" + ActionLinodeConfigCreate EventAction = "linode_config_create" + ActionLinodeConfigDelete EventAction = "linode_config_delete" + ActionLinodeConfigUpdate EventAction = "linode_config_update" + ActionLishBoot EventAction = "lish_boot" + ActionLKENodeCreate EventAction = "lke_node_create" + ActionLongviewClientCreate EventAction = "longviewclient_create" + ActionLongviewClientDelete EventAction = "longviewclient_delete" + ActionLongviewClientUpdate EventAction = "longviewclient_update" + ActionManagedDisabled EventAction = "managed_disabled" + ActionManagedEnabled EventAction = "managed_enabled" + ActionManagedServiceCreate EventAction = "managed_service_create" + ActionManagedServiceDelete EventAction = "managed_service_delete" + ActionNodebalancerCreate EventAction = "nodebalancer_create" + ActionNodebalancerDelete EventAction = "nodebalancer_delete" + ActionNodebalancerUpdate EventAction = "nodebalancer_update" + ActionNodebalancerConfigCreate EventAction = "nodebalancer_config_create" + ActionNodebalancerConfigDelete EventAction = "nodebalancer_config_delete" + ActionNodebalancerConfigUpdate EventAction = "nodebalancer_config_update" + ActionNodebalancerNodeCreate EventAction = "nodebalancer_node_create" + ActionNodebalancerNodeDelete EventAction = "nodebalancer_node_delete" + ActionNodebalancerNodeUpdate EventAction = "nodebalancer_node_update" + ActionOAuthClientCreate EventAction = "oauth_client_create" + ActionOAuthClientDelete EventAction = "oauth_client_delete" + ActionOAuthClientSecretReset EventAction = "oauth_client_secret_reset" //#nosec G101 + ActionOAuthClientUpdate EventAction = "oauth_client_update" + ActionPaymentMethodAdd EventAction = "payment_method_add" + ActionPaymentSubmitted EventAction = "payment_submitted" + ActionPasswordReset EventAction = "password_reset" + ActionProfileUpdate EventAction = "profile_update" + ActionStackScriptCreate EventAction = "stackscript_create" + ActionStackScriptDelete EventAction = "stackscript_delete" + ActionStackScriptUpdate EventAction = "stackscript_update" + ActionStackScriptPublicize EventAction = "stackscript_publicize" + ActionStackScriptRevise EventAction = "stackscript_revise" + ActionTagCreate EventAction = "tag_create" + ActionTagDelete EventAction = "tag_delete" + ActionTFADisabled EventAction = "tfa_disabled" + ActionTFAEnabled EventAction = "tfa_enabled" + ActionTicketAttachmentUpload EventAction = "ticket_attachment_upload" + ActionTicketCreate EventAction = "ticket_create" + ActionTicketUpdate EventAction = "ticket_update" + ActionTokenCreate EventAction = "token_create" + ActionTokenDelete EventAction = "token_delete" + ActionTokenUpdate EventAction = "token_update" + ActionUserCreate EventAction = "user_create" + ActionUserDelete EventAction = "user_delete" + ActionUserUpdate EventAction = "user_update" + ActionUserSSHKeyAdd EventAction = "user_ssh_key_add" + ActionUserSSHKeyDelete EventAction = "user_ssh_key_delete" + ActionUserSSHKeyUpdate EventAction = "user_ssh_key_update" + ActionVLANAttach EventAction = "vlan_attach" + ActionVLANDetach EventAction = "vlan_detach" + ActionVolumeAttach EventAction = "volume_attach" + ActionVolumeClone EventAction = "volume_clone" + ActionVolumeCreate EventAction = "volume_create" + ActionVolumeDelte EventAction = "volume_delete" + ActionVolumeUpdate EventAction = "volume_update" + ActionVolumeDetach EventAction = "volume_detach" + ActionVolumeResize EventAction = "volume_resize" ) // EntityType constants start with Entity and include Linode API Event Entity Types diff --git a/account_invoices.go b/account_invoices.go index 26529aaa7..6833ed257 100644 --- a/account_invoices.go +++ b/account_invoices.go @@ -25,6 +25,8 @@ type InvoiceItem struct { UnitPrice int `json:"unitprice"` Quantity int `json:"quantity"` Amount float32 `json:"amount"` + Tax float32 `json:"tax"` + Region *string `json:"region"` From *time.Time `json:"-"` To *time.Time `json:"-"` } diff --git a/test/integration/types_test.go b/test/integration/types_test.go index 519f08c9c..b787e45fb 100644 --- a/test/integration/types_test.go +++ b/test/integration/types_test.go @@ -50,3 +50,47 @@ func TestTypes_List(t *testing.T) { t.Errorf("Expected a list of images, but got none %v", i) } } + +func TestTypes_RegionSpecific(t *testing.T) { + client, teardown := createTestClient(t, "fixtures/TestTypes_RegionSpecific") + defer teardown() + + validateOverride := func(override linodego.LinodeRegionPrice) { + if override.ID == "" { + t.Fatal("Expected region; got nil") + } + + if override.Monthly <= 0 { + t.Fatalf("Expected monthly cost; got %f", override.Monthly) + } + + if override.Hourly <= 0 { + t.Fatalf("Expected hourly cost; got %f", override.Hourly) + } + } + + types, err := client.ListTypes(context.Background(), nil) + if err != nil { + t.Errorf("Error listing images, expected struct, got error %v", err) + } + + var targetType *linodego.LinodeType + for _, t := range types { + if t.RegionPrices != nil && len(t.RegionPrices) > 0 { + targetType = &t + } + } + + if targetType == nil { + t.Fatal("expected type with region override, got none") + } + + // Validate overrides + for _, override := range targetType.RegionPrices { + validateOverride(override) + } + + for _, override := range targetType.Addons.Backups.RegionPrices { + validateOverride(override) + } +} diff --git a/types.go b/types.go index 01d655340..1d8341367 100644 --- a/types.go +++ b/types.go @@ -10,18 +10,19 @@ import ( // LinodeType represents a linode type object type LinodeType struct { - ID string `json:"id"` - Disk int `json:"disk"` - Class LinodeTypeClass `json:"class"` // enum: nanode, standard, highmem, dedicated, gpu - Price *LinodePrice `json:"price"` - Label string `json:"label"` - Addons *LinodeAddons `json:"addons"` - NetworkOut int `json:"network_out"` - Memory int `json:"memory"` - Transfer int `json:"transfer"` - VCPUs int `json:"vcpus"` - GPUs int `json:"gpus"` - Successor string `json:"successor"` + ID string `json:"id"` + Disk int `json:"disk"` + Class LinodeTypeClass `json:"class"` // enum: nanode, standard, highmem, dedicated, gpu + Price *LinodePrice `json:"price"` + Label string `json:"label"` + Addons *LinodeAddons `json:"addons"` + RegionPrices []LinodeRegionPrice `json:"region_prices"` + NetworkOut int `json:"network_out"` + Memory int `json:"memory"` + Transfer int `json:"transfer"` + VCPUs int `json:"vcpus"` + GPUs int `json:"gpus"` + Successor string `json:"successor"` } // LinodePrice represents a linode type price object @@ -32,7 +33,8 @@ type LinodePrice struct { // LinodeBackupsAddon represents a linode backups addon object type LinodeBackupsAddon struct { - Price *LinodePrice `json:"price"` + Price *LinodePrice `json:"price"` + RegionPrices []LinodeRegionPrice `json:"region_prices"` } // LinodeAddons represent the linode addons object @@ -40,6 +42,14 @@ type LinodeAddons struct { Backups *LinodeBackupsAddon `json:"backups"` } +// LinodeRegionPrice represents an individual type or addon +// price exception for a region. +type LinodeRegionPrice struct { + ID string `json:"id"` + Hourly float32 `json:"hourly"` + Monthly float32 `json:"monthly"` +} + // LinodeTypeClass constants start with Class and include Linode API Instance Type Classes type LinodeTypeClass string From e52d737b4579ca5bbc29c8a0de04b44f488754f7 Mon Sep 17 00:00:00 2001 From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com> Date: Fri, 22 Sep 2023 10:56:06 -0400 Subject: [PATCH 2/3] Add GetAccountTransfer (#382) --- account_transfer.go | 33 ++++++++++++++++++ test/integration/account_transfer_test.go | 41 +++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 account_transfer.go create mode 100644 test/integration/account_transfer_test.go diff --git a/account_transfer.go b/account_transfer.go new file mode 100644 index 000000000..115573021 --- /dev/null +++ b/account_transfer.go @@ -0,0 +1,33 @@ +package linodego + +import "context" + +// AccountTransfer represents an Account's network utilization for the current month. +type AccountTransfer struct { + Billable int `json:"billable"` + Quota int `json:"quota"` + Used int `json:"used"` + + RegionTransfers []AccountTransferRegion `json:"region_transfers"` +} + +// AccountTransferRegion represents an Account's network utilization for the current month +// in a given region. +type AccountTransferRegion struct { + ID string `json:"id"` + Billable int `json:"billable"` + Quota int `json:"quota"` + Used int `json:"used"` +} + +// GetAccountTransfer gets current Account's network utilization for the current month. +func (c *Client) GetAccountTransfer(ctx context.Context) (*AccountTransfer, error) { + req := c.R(ctx).SetResult(&AccountTransfer{}) + e := "account/transfer" + r, err := coupleAPIErrors(req.Get(e)) + if err != nil { + return nil, err + } + + return r.Result().(*AccountTransfer), nil +} diff --git a/test/integration/account_transfer_test.go b/test/integration/account_transfer_test.go new file mode 100644 index 000000000..1cac97b72 --- /dev/null +++ b/test/integration/account_transfer_test.go @@ -0,0 +1,41 @@ +package integration + +import ( + "context" + "reflect" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/jarcoal/httpmock" + "github.com/linode/linodego" +) + +func TestAccount_getTransfer(t *testing.T) { + client := createMockClient(t) + + desiredResponse := linodego.AccountTransfer{ + Billable: 123, + Quota: 456, + Used: 789, + RegionTransfers: []linodego.AccountTransferRegion{ + { + ID: "us-southeast", + Billable: 987, + Quota: 654, + Used: 3211, + }, + }, + } + + httpmock.RegisterRegexpResponder("GET", mockRequestURL(t, "/account/transfer"), + httpmock.NewJsonResponderOrPanic(200, &desiredResponse)) + + questions, err := client.GetAccountTransfer(context.Background()) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(*questions, desiredResponse) { + t.Fatalf("actual response does not equal desired response: %s", cmp.Diff(questions, desiredResponse)) + } +} From e6f92fd4be6062c3526fbde3c7f8851fdc5f8a92 Mon Sep 17 00:00:00 2001 From: Lena Garber <114949949+lgarber-akamai@users.noreply.github.com> Date: Mon, 25 Sep 2023 11:44:32 -0400 Subject: [PATCH 3/3] Update region-specific typing tests (#387) --- .../fixtures/TestTypes_RegionSpecific.yaml | 269 ++++++++++++++++++ test/integration/types_test.go | 5 +- 2 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 test/integration/fixtures/TestTypes_RegionSpecific.yaml diff --git a/test/integration/fixtures/TestTypes_RegionSpecific.yaml b/test/integration/fixtures/TestTypes_RegionSpecific.yaml new file mode 100644 index 000000000..3482e58e5 --- /dev/null +++ b/test/integration/fixtures/TestTypes_RegionSpecific.yaml @@ -0,0 +1,269 @@ +--- +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/linode/types + method: GET + response: + body: '{"data": [{"id": "g6-nanode-1", "label": "Nanode 1GB", "price": {"hourly": + 0.0075, "monthly": 5.0}, "region_prices": [{"id": "us-east", "hourly": 0.009, + "monthly": 6.0}], "addons": {"backups": {"price": {"hourly": 0.003, "monthly": + 2.0}, "region_prices": [{"id": "us-east", "hourly": 0.0036, "monthly": 2.4}]}}, + "memory": 1024, "disk": 25600, "transfer": 1000, "vcpus": 1, "gpus": 0, "network_out": + 1000, "class": "nanode", "successor": null}, {"id": "g6-standard-1", "label": + "Linode 2GB", "price": {"hourly": 0.015, "monthly": 10.0}, "region_prices": + [{"id": "us-east", "hourly": 0.018, "monthly": 12.0}], "addons": {"backups": + {"price": {"hourly": 0.004, "monthly": 2.5}, "region_prices": [{"id": "us-east", + "hourly": 0.0048, "monthly": 3.0}]}}, "memory": 2048, "disk": 51200, "transfer": + 2000, "vcpus": 1, "gpus": 0, "network_out": 2000, "class": "standard", "successor": + null}, {"id": "g6-standard-2", "label": "Linode 4GB", "price": {"hourly": 0.03, + "monthly": 20.0}, "region_prices": [{"id": "us-east", "hourly": 0.036, "monthly": + 24.0}], "addons": {"backups": {"price": {"hourly": 0.008, "monthly": 5.0}, "region_prices": + [{"id": "us-east", "hourly": 0.0096, "monthly": 6.0}]}}, "memory": 4096, "disk": + 81920, "transfer": 4000, "vcpus": 2, "gpus": 0, "network_out": 4000, "class": + "standard", "successor": null}, {"id": "g6-standard-4", "label": "Linode 8GB", + "price": {"hourly": 0.06, "monthly": 40.0}, "region_prices": [{"id": "us-east", + "hourly": 0.072, "monthly": 48.0}], "addons": {"backups": {"price": {"hourly": + 0.015, "monthly": 10.0}, "region_prices": [{"id": "us-east", "hourly": 0.018, + "monthly": 12.0}]}}, "memory": 8192, "disk": 163840, "transfer": 5000, "vcpus": + 4, "gpus": 0, "network_out": 5000, "class": "standard", "successor": null}, + {"id": "g6-standard-6", "label": "Linode 16GB", "price": {"hourly": 0.12, "monthly": + 80.0}, "region_prices": [{"id": "us-east", "hourly": 0.144, "monthly": 96.0}], + "addons": {"backups": {"price": {"hourly": 0.03, "monthly": 20.0}, "region_prices": + [{"id": "us-east", "hourly": 0.036, "monthly": 24.0}]}}, "memory": 16384, "disk": + 327680, "transfer": 8000, "vcpus": 6, "gpus": 0, "network_out": 6000, "class": + "standard", "successor": null}, {"id": "g6-standard-8", "label": "Linode 32GB", + "price": {"hourly": 0.24, "monthly": 160.0}, "region_prices": [{"id": "us-east", + "hourly": 0.288, "monthly": 192.0}], "addons": {"backups": {"price": {"hourly": + 0.06, "monthly": 40.0}, "region_prices": [{"id": "us-east", "hourly": 0.072, + "monthly": 48.0}]}}, "memory": 32768, "disk": 655360, "transfer": 16000, "vcpus": + 8, "gpus": 0, "network_out": 7000, "class": "standard", "successor": null}, + {"id": "g6-standard-16", "label": "Linode 64GB", "price": {"hourly": 0.48, "monthly": + 320.0}, "region_prices": [{"id": "us-east", "hourly": 0.576, "monthly": 384.0}], + "addons": {"backups": {"price": {"hourly": 0.12, "monthly": 80.0}, "region_prices": + [{"id": "us-east", "hourly": 0.144, "monthly": 96.0}]}}, "memory": 65536, "disk": + 1310720, "transfer": 20000, "vcpus": 16, "gpus": 0, "network_out": 9000, "class": + "standard", "successor": null}, {"id": "g6-standard-20", "label": "Linode 96GB", + "price": {"hourly": 0.72, "monthly": 480.0}, "region_prices": [{"id": "us-east", + "hourly": 0.864, "monthly": 576.0}], "addons": {"backups": {"price": {"hourly": + 0.18, "monthly": 120.0}, "region_prices": [{"id": "us-east", "hourly": 0.216, + "monthly": 144.0}]}}, "memory": 98304, "disk": 1966080, "transfer": 20000, "vcpus": + 20, "gpus": 0, "network_out": 10000, "class": "standard", "successor": null}, + {"id": "g6-standard-24", "label": "Linode 128GB", "price": {"hourly": 0.96, + "monthly": 640.0}, "region_prices": [{"id": "us-east", "hourly": 1.152, "monthly": + 768.0}], "addons": {"backups": {"price": {"hourly": 0.24, "monthly": 160.0}, + "region_prices": [{"id": "us-east", "hourly": 0.288, "monthly": 192.0}]}}, "memory": + 131072, "disk": 2621440, "transfer": 20000, "vcpus": 24, "gpus": 0, "network_out": + 11000, "class": "standard", "successor": null}, {"id": "g6-standard-32", "label": + "Linode 192GB", "price": {"hourly": 1.44, "monthly": 960.0}, "region_prices": + [{"id": "us-east", "hourly": 1.728, "monthly": 1152.0}], "addons": {"backups": + {"price": {"hourly": 0.36, "monthly": 240.0}, "region_prices": [{"id": "us-east", + "hourly": 0.432, "monthly": 288.0}]}}, "memory": 196608, "disk": 3932160, "transfer": + 20000, "vcpus": 32, "gpus": 0, "network_out": 12000, "class": "standard", "successor": + null}, {"id": "g7-highmem-1", "label": "Linode 24GB", "price": {"hourly": 0.09, + "monthly": 60.0}, "region_prices": [{"id": "us-east", "hourly": 0.108, "monthly": + 72.0}], "addons": {"backups": {"price": {"hourly": 0.0075, "monthly": 5.0}, + "region_prices": [{"id": "us-east", "hourly": 0.009, "monthly": 6.0}]}}, "memory": + 24576, "disk": 20480, "transfer": 5000, "vcpus": 2, "gpus": 0, "network_out": + 5000, "class": "highmem", "successor": null}, {"id": "g7-highmem-2", "label": + "Linode 48GB", "price": {"hourly": 0.18, "monthly": 120.0}, "region_prices": + [{"id": "us-east", "hourly": 0.216, "monthly": 144.0}], "addons": {"backups": + {"price": {"hourly": 0.015, "monthly": 10.0}, "region_prices": [{"id": "us-east", + "hourly": 0.018, "monthly": 12.0}]}}, "memory": 49152, "disk": 40960, "transfer": + 6000, "vcpus": 2, "gpus": 0, "network_out": 6000, "class": "highmem", "successor": + null}, {"id": "g7-highmem-4", "label": "Linode 90GB", "price": {"hourly": 0.36, + "monthly": 240.0}, "region_prices": [{"id": "us-east", "hourly": 0.432, "monthly": + 288.0}], "addons": {"backups": {"price": {"hourly": 0.03, "monthly": 20.0}, + "region_prices": [{"id": "us-east", "hourly": 0.036, "monthly": 24.0}]}}, "memory": + 92160, "disk": 92160, "transfer": 7000, "vcpus": 4, "gpus": 0, "network_out": + 7000, "class": "highmem", "successor": null}, {"id": "g7-highmem-8", "label": + "Linode 150GB", "price": {"hourly": 0.72, "monthly": 480.0}, "region_prices": + [{"id": "us-east", "hourly": 0.864, "monthly": 576.0}], "addons": {"backups": + {"price": {"hourly": 0.06, "monthly": 40.0}, "region_prices": [{"id": "us-east", + "hourly": 0.072, "monthly": 48.0}]}}, "memory": 153600, "disk": 204800, "transfer": + 8000, "vcpus": 8, "gpus": 0, "network_out": 8000, "class": "highmem", "successor": + null}, {"id": "g7-highmem-16", "label": "Linode 300GB", "price": {"hourly": + 1.44, "monthly": 960.0}, "region_prices": [{"id": "us-east", "hourly": 1.728, + "monthly": 1152.0}], "addons": {"backups": {"price": {"hourly": 0.12, "monthly": + 80.0}, "region_prices": [{"id": "us-east", "hourly": 0.144, "monthly": 96.0}]}}, + "memory": 307200, "disk": 348160, "transfer": 9000, "vcpus": 16, "gpus": 0, + "network_out": 9000, "class": "highmem", "successor": null}, {"id": "g6-dedicated-2", + "label": "Dedicated 4GB", "price": {"hourly": 0.045, "monthly": 30.0}, "region_prices": + [{"id": "us-east", "hourly": 0.054, "monthly": 36.0}], "addons": {"backups": + {"price": {"hourly": 0.008, "monthly": 5.0}, "region_prices": [{"id": "us-east", + "hourly": 0.0096, "monthly": 6.0}]}}, "memory": 4096, "disk": 81920, "transfer": + 4000, "vcpus": 2, "gpus": 0, "network_out": 4000, "class": "dedicated", "successor": + null}, {"id": "g6-dedicated-4", "label": "Dedicated 8GB", "price": {"hourly": + 0.09, "monthly": 60.0}, "region_prices": [{"id": "us-east", "hourly": 0.108, + "monthly": 72.0}], "addons": {"backups": {"price": {"hourly": 0.015, "monthly": + 10.0}, "region_prices": [{"id": "us-east", "hourly": 0.018, "monthly": 12.0}]}}, + "memory": 8192, "disk": 163840, "transfer": 5000, "vcpus": 4, "gpus": 0, "network_out": + 5000, "class": "dedicated", "successor": null}, {"id": "g6-dedicated-8", "label": + "Dedicated 16GB", "price": {"hourly": 0.18, "monthly": 120.0}, "region_prices": + [{"id": "us-east", "hourly": 0.216, "monthly": 144.0}], "addons": {"backups": + {"price": {"hourly": 0.03, "monthly": 20.0}, "region_prices": [{"id": "us-east", + "hourly": 0.036, "monthly": 24.0}]}}, "memory": 16384, "disk": 327680, "transfer": + 8000, "vcpus": 8, "gpus": 0, "network_out": 6000, "class": "dedicated", "successor": + null}, {"id": "g6-dedicated-16", "label": "Dedicated 32GB", "price": {"hourly": + 0.36, "monthly": 240.0}, "region_prices": [{"id": "us-east", "hourly": 0.432, + "monthly": 288.0}], "addons": {"backups": {"price": {"hourly": 0.06, "monthly": + 40.0}, "region_prices": [{"id": "us-east", "hourly": 0.072, "monthly": 48.0}]}}, + "memory": 32768, "disk": 655360, "transfer": 16000, "vcpus": 16, "gpus": 0, + "network_out": 7000, "class": "dedicated", "successor": null}, {"id": "g6-dedicated-32", + "label": "Dedicated 64GB", "price": {"hourly": 0.72, "monthly": 480.0}, "region_prices": + [{"id": "us-east", "hourly": 0.864, "monthly": 576.0}], "addons": {"backups": + {"price": {"hourly": 0.12, "monthly": 80.0}, "region_prices": [{"id": "us-east", + "hourly": 0.144, "monthly": 96.0}]}}, "memory": 65536, "disk": 1310720, "transfer": + 20000, "vcpus": 32, "gpus": 0, "network_out": 8000, "class": "dedicated", "successor": + null}, {"id": "g6-dedicated-48", "label": "Dedicated 96GB", "price": {"hourly": + 1.08, "monthly": 720.0}, "region_prices": [{"id": "us-east", "hourly": 1.296, + "monthly": 864.0}], "addons": {"backups": {"price": {"hourly": 0.18, "monthly": + 120.0}, "region_prices": [{"id": "us-east", "hourly": 0.216, "monthly": 144.0}]}}, + "memory": 98304, "disk": 1966080, "transfer": 20000, "vcpus": 48, "gpus": 0, + "network_out": 9000, "class": "dedicated", "successor": null}, {"id": "g6-sawhorse-64", + "label": "Sawhorse 192GB", "price": {"hourly": 4.5, "monthly": 3000.0}, "region_prices": + [{"id": "us-east", "hourly": 5.4, "monthly": 3600.0}], "addons": {"backups": + {"price": {"hourly": 0.0, "monthly": 0.0}, "region_prices": [{"id": "us-east", + "hourly": 0.0, "monthly": 0.0}]}}, "memory": 196608, "disk": 8388608, "transfer": + 9000, "vcpus": 64, "gpus": 0, "network_out": 10000, "class": "sawhorse", "successor": + null}, {"id": "g6-dedicated-50", "label": "Dedicated 128GB", "price": {"hourly": + 1.44, "monthly": 960.0}, "region_prices": [{"id": "us-east", "hourly": 1.728, + "monthly": 1152.0}], "addons": {"backups": {"price": {"hourly": 0.24, "monthly": + 160.0}, "region_prices": [{"id": "us-east", "hourly": 0.288, "monthly": 192.0}]}}, + "memory": 131072, "disk": 2560000, "transfer": 10000, "vcpus": 50, "gpus": 0, + "network_out": 10000, "class": "dedicated", "successor": null}, {"id": "g6-dedicated-56", + "label": "Dedicated 256GB", "price": {"hourly": 2.88, "monthly": 1920.0}, "region_prices": + [{"id": "us-east", "hourly": 3.456, "monthly": 2304.0}], "addons": {"backups": + {"price": {"hourly": 0.3, "monthly": 200.0}, "region_prices": [{"id": "us-east", + "hourly": 0.36, "monthly": 240.0}]}}, "memory": 262144, "disk": 5120000, "transfer": + 11000, "vcpus": 56, "gpus": 0, "network_out": 11000, "class": "dedicated", "successor": + null}, {"id": "g6-dedicated-64", "label": "Dedicated 512GB", "price": {"hourly": + 5.76, "monthly": 3840.0}, "region_prices": [{"id": "us-east", "hourly": 6.912, + "monthly": 4608.0}], "addons": {"backups": {"price": {"hourly": 0.36, "monthly": + 240.0}, "region_prices": [{"id": "us-east", "hourly": 0.432, "monthly": 288.0}]}}, + "memory": 524288, "disk": 7372800, "transfer": 12000, "vcpus": 64, "gpus": 0, + "network_out": 12000, "class": "dedicated", "successor": null}, {"id": "g1-gpu-rtx6000-1", + "label": "Dedicated 32GB + RTX6000 GPU x1", "price": {"hourly": 1.5, "monthly": + 1000.0}, "region_prices": [{"id": "us-east", "hourly": 1.8, "monthly": 1200.0}], + "addons": {"backups": {"price": {"hourly": 0.06, "monthly": 40.0}, "region_prices": + [{"id": "us-east", "hourly": 0.072, "monthly": 48.0}]}}, "memory": 32768, "disk": + 655360, "transfer": 16000, "vcpus": 8, "gpus": 0, "network_out": 10000, "class": + "gpu", "successor": null}, {"id": "g1-gpu-rtx6000-2", "label": "Dedicated 64GB + + RTX6000 GPU x2", "price": {"hourly": 3.0, "monthly": 2000.0}, "region_prices": + [{"id": "us-east", "hourly": 3.6, "monthly": 2400.0}], "addons": {"backups": + {"price": {"hourly": 0.12, "monthly": 80.0}, "region_prices": [{"id": "us-east", + "hourly": 0.144, "monthly": 96.0}]}}, "memory": 65536, "disk": 1310720, "transfer": + 20000, "vcpus": 16, "gpus": 0, "network_out": 10000, "class": "gpu", "successor": + null}, {"id": "g1-gpu-rtx6000-3", "label": "Dedicated 96GB + RTX6000 GPU x3", + "price": {"hourly": 4.5, "monthly": 3000.0}, "region_prices": [{"id": "us-east", + "hourly": 5.4, "monthly": 3600.0}], "addons": {"backups": {"price": {"hourly": + 0.18, "monthly": 160.0}, "region_prices": [{"id": "us-east", "hourly": 0.216, + "monthly": 192.0}]}}, "memory": 98304, "disk": 1966080, "transfer": 20000, "vcpus": + 20, "gpus": 0, "network_out": 10000, "class": "gpu", "successor": null}, {"id": + "g1-gpu-rtx6000-4", "label": "Dedicated 128GB + RTX6000 GPU x4", "price": {"hourly": + 6.0, "monthly": 4000.0}, "region_prices": [{"id": "us-east", "hourly": 7.2, + "monthly": 4800.0}], "addons": {"backups": {"price": {"hourly": 0.24, "monthly": + 320.0}, "region_prices": [{"id": "us-east", "hourly": 0.288, "monthly": 384.0}]}}, + "memory": 131072, "disk": 2621440, "transfer": 20000, "vcpus": 24, "gpus": 0, + "network_out": 10000, "class": "gpu", "successor": null}, {"id": "g6-premium-2", + "label": "Premium 4GB", "price": {"hourly": 0.05, "monthly": 36.0}, "region_prices": + [{"id": "us-east", "hourly": 0.06, "monthly": 43.2}], "addons": {"backups": + {"price": {"hourly": 0.008, "monthly": 5.0}, "region_prices": [{"id": "us-east", + "hourly": 0.0096, "monthly": 6.0}]}}, "memory": 4096, "disk": 81920, "transfer": + 4000, "vcpus": 2, "gpus": 0, "network_out": 4000, "class": "premium", "successor": + null}, {"id": "g6-premium-4", "label": "Premium 8GB", "price": {"hourly": 0.11, + "monthly": 72.0}, "region_prices": [{"id": "us-east", "hourly": 0.132, "monthly": + 86.4}], "addons": {"backups": {"price": {"hourly": 0.015, "monthly": 10.0}, + "region_prices": [{"id": "us-east", "hourly": 0.018, "monthly": 12.0}]}}, "memory": + 8192, "disk": 163840, "transfer": 5000, "vcpus": 4, "gpus": 0, "network_out": + 5000, "class": "premium", "successor": null}, {"id": "g6-premium-8", "label": + "Premium 16GB", "price": {"hourly": 0.22, "monthly": 144.0}, "region_prices": + [{"id": "us-east", "hourly": 0.264, "monthly": 172.8}], "addons": {"backups": + {"price": {"hourly": 0.03, "monthly": 20.0}, "region_prices": [{"id": "us-east", + "hourly": 0.036, "monthly": 24.0}]}}, "memory": 16384, "disk": 327680, "transfer": + 6000, "vcpus": 8, "gpus": 0, "network_out": 6000, "class": "premium", "successor": + null}, {"id": "g6-premium-16", "label": "Premium 32GB", "price": {"hourly": + 0.43, "monthly": 288.0}, "region_prices": [{"id": "us-east", "hourly": 0.516, + "monthly": 345.6}], "addons": {"backups": {"price": {"hourly": 0.06, "monthly": + 40.0}, "region_prices": [{"id": "us-east", "hourly": 0.072, "monthly": 48.0}]}}, + "memory": 32768, "disk": 655360, "transfer": 7000, "vcpus": 16, "gpus": 0, "network_out": + 7000, "class": "premium", "successor": null}, {"id": "g6-premium-32", "label": + "Premium 64GB", "price": {"hourly": 0.86, "monthly": 576.0}, "region_prices": + [{"id": "us-east", "hourly": 1.032, "monthly": 691.2}], "addons": {"backups": + {"price": {"hourly": 0.12, "monthly": 80.0}, "region_prices": [{"id": "us-east", + "hourly": 0.144, "monthly": 96.0}]}}, "memory": 65536, "disk": 1310720, "transfer": + 8000, "vcpus": 32, "gpus": 0, "network_out": 8000, "class": "premium", "successor": + null}, {"id": "g6-premium-48", "label": "Premium 96GB", "price": {"hourly": + 1.3, "monthly": 864.0}, "region_prices": [{"id": "us-east", "hourly": 1.56, + "monthly": 1036.8}], "addons": {"backups": {"price": {"hourly": 0.18, "monthly": + 120.0}, "region_prices": [{"id": "us-east", "hourly": 0.216, "monthly": 144.0}]}}, + "memory": 98304, "disk": 1966080, "transfer": 9000, "vcpus": 48, "gpus": 0, + "network_out": 9000, "class": "premium", "successor": null}, {"id": "g6-premium-50", + "label": "Premium 128GB", "price": {"hourly": 1.73, "monthly": 1152.0}, "region_prices": + [{"id": "us-east", "hourly": 2.076, "monthly": 1382.4}], "addons": {"backups": + {"price": {"hourly": 0.24, "monthly": 160.0}, "region_prices": [{"id": "us-east", + "hourly": 0.288, "monthly": 192.0}]}}, "memory": 131072, "disk": 2560000, "transfer": + 10000, "vcpus": 50, "gpus": 0, "network_out": 10000, "class": "premium", "successor": + null}, {"id": "g6-premium-56", "label": "Premium 256GB", "price": {"hourly": + 3.46, "monthly": 2304.0}, "region_prices": [{"id": "us-east", "hourly": 4.152, + "monthly": 2764.8}], "addons": {"backups": {"price": {"hourly": 0.3, "monthly": + 200.0}, "region_prices": [{"id": "us-east", "hourly": 0.36, "monthly": 240.0}]}}, + "memory": 262144, "disk": 5120000, "transfer": 11000, "vcpus": 56, "gpus": 0, + "network_out": 11000, "class": "premium", "successor": null}, {"id": "g6-premium-64", + "label": "Premium 512GB", "price": {"hourly": 6.91, "monthly": 4608.0}, "region_prices": + [{"id": "us-east", "hourly": 8.292, "monthly": 5529.6}], "addons": {"backups": + {"price": {"hourly": 0.36, "monthly": 240.0}, "region_prices": [{"id": "us-east", + "hourly": 0.432, "monthly": 288.0}]}}, "memory": 524288, "disk": 7372800, "transfer": + 12000, "vcpus": 64, "gpus": 0, "network_out": 12000, "class": "premium", "successor": + null}], "page": 1, "pages": 1, "results": 46}' + 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 + Cache-Control: + - private, max-age=900 + - private, max-age=60, s-maxage=60 + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Accept-Encoding + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - '*' + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "1200" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" diff --git a/test/integration/types_test.go b/test/integration/types_test.go index b787e45fb..3331c6739 100644 --- a/test/integration/types_test.go +++ b/test/integration/types_test.go @@ -76,8 +76,11 @@ func TestTypes_RegionSpecific(t *testing.T) { var targetType *linodego.LinodeType for _, t := range types { - if t.RegionPrices != nil && len(t.RegionPrices) > 0 { + if t.RegionPrices != nil && + len(t.RegionPrices) > 0 && + t.RegionPrices[0].Hourly > 0 { targetType = &t + break } }