diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41f0cd15f..20e34813d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version: stable @@ -44,13 +44,13 @@ jobs: submodules: 'recursive' - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: 'stable' - run: go version - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.x' diff --git a/.github/workflows/integration_tests_pr.yml b/.github/workflows/integration_tests_pr.yml index 881d7a16e..9efd1328f 100644 --- a/.github/workflows/integration_tests_pr.yml +++ b/.github/workflows/integration_tests_pr.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'workflow_dispatch' && inputs.sha != '' steps: - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version: 'stable' - run: go version @@ -71,7 +71,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/github-script@v7 + - uses: actions/github-script@v8 id: update-check-run if: ${{ inputs.pull_request_number != '' && fromJson(steps.commit-hash.outputs.data).repository.pullRequest.headRef.target.oid == inputs.sha }} env: @@ -132,7 +132,7 @@ jobs: steps: - name: Set up Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: '3.x' diff --git a/.github/workflows/nightly_smoke_tests.yml b/.github/workflows/nightly_smoke_tests.yml index 4401e0bd2..1f2ee6908 100644 --- a/.github/workflows/nightly_smoke_tests.yml +++ b/.github/workflows/nightly_smoke_tests.yml @@ -25,7 +25,7 @@ jobs: ref: ${{ github.event.inputs.sha || github.ref }} - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: '1.x' diff --git a/.github/workflows/release-cross-repo-test.yml b/.github/workflows/release-cross-repo-test.yml index eac3e4737..1f39c2a7c 100644 --- a/.github/workflows/release-cross-repo-test.yml +++ b/.github/workflows/release-cross-repo-test.yml @@ -15,7 +15,7 @@ jobs: submodules: 'recursive' - name: Set up Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version: 'stable' - run: go version diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 00d129fd8..34b90e83b 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -8,7 +8,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - uses: actions/stale@v10 id: stale with: stale-issue-message: 'This issue is stale because it has been open 30 days with diff --git a/account.go b/account.go index 8aee3cb91..9201fd62c 100644 --- a/account.go +++ b/account.go @@ -63,7 +63,7 @@ func (i Account) GetUpdateOptions() (o AccountUpdateOptions) { o.TaxID = i.TaxID o.Zip = i.Zip - return + return o } // UnmarshalJSON implements the json.Unmarshaler interface diff --git a/account_agreements.go b/account_agreements.go index d7437b4aa..795244a4a 100644 --- a/account_agreements.go +++ b/account_agreements.go @@ -22,7 +22,7 @@ func (i AccountAgreements) GetUpdateOptions() (o AccountAgreementsUpdateOptions) o.MasterServiceAgreement = i.MasterServiceAgreement o.PrivacyPolicy = i.PrivacyPolicy - return + return o } // GetAccountAgreements gets all agreements and their acceptance status for the Account. diff --git a/account_betas.go b/account_betas.go index 1a498e808..2e23c9284 100644 --- a/account_betas.go +++ b/account_betas.go @@ -8,7 +8,9 @@ import ( "github.com/linode/linodego/internal/parseabletime" ) -// The details and enrollment information of a Beta program that an account is enrolled in. +// AccountBetaProgram represents an enrolled Account Beta Program object, +// which contains the details and enrollment information of a Beta program +// that an account is enrolled in. type AccountBetaProgram struct { Label string `json:"label"` ID string `json:"id"` diff --git a/account_maintenance.go b/account_maintenance.go index 96be1ad35..6e50cab5a 100644 --- a/account_maintenance.go +++ b/account_maintenance.go @@ -28,7 +28,7 @@ type AccountMaintenance struct { When *time.Time `json:"when"` } -// The entity being affected by maintenance +// Entity represents the entity being affected by maintenance type Entity struct { ID int `json:"id"` Label string `json:"label"` diff --git a/account_oauth_client.go b/account_oauth_client.go index ee0c48099..7b0e7795a 100644 --- a/account_oauth_client.go +++ b/account_oauth_client.go @@ -68,7 +68,7 @@ func (i OAuthClient) GetCreateOptions() (o OAuthClientCreateOptions) { o.Label = i.Label o.Public = i.Public - return + return o } // GetUpdateOptions converts a OAuthClient to OAuthClientUpdateOptions for use in UpdateOAuthClient @@ -77,7 +77,7 @@ func (i OAuthClient) GetUpdateOptions() (o OAuthClientUpdateOptions) { o.Label = i.Label o.Public = i.Public - return + return o } // ListOAuthClients lists OAuthClients diff --git a/account_payments.go b/account_payments.go index 40feebc13..af2488e9e 100644 --- a/account_payments.go +++ b/account_payments.go @@ -53,7 +53,7 @@ func (i *Payment) UnmarshalJSON(b []byte) error { // GetCreateOptions converts a Payment to PaymentCreateOptions for use in CreatePayment func (i Payment) GetCreateOptions() (o PaymentCreateOptions) { o.USD = i.USD - return + return o } // ListPayments lists Payments diff --git a/account_user_grants.go b/account_user_grants.go index 60fd76232..5314c79b9 100644 --- a/account_user_grants.go +++ b/account_user_grants.go @@ -20,7 +20,6 @@ type GlobalUserGrants struct { AddLinodes bool `json:"add_linodes"` AddLongview bool `json:"add_longview"` AddNodeBalancers bool `json:"add_nodebalancers"` - AddPlacementGroups bool `json:"add_placement_groups"` AddStackScripts bool `json:"add_stackscripts"` AddVolumes bool `json:"add_volumes"` AddVPCs bool `json:"add_vpcs"` diff --git a/account_users.go b/account_users.go index d6e46ef09..13e71c520 100644 --- a/account_users.go +++ b/account_users.go @@ -98,7 +98,7 @@ func (i User) GetCreateOptions() (o UserCreateOptions) { o.Email = i.Email o.Restricted = i.Restricted - return + return o } // GetUpdateOptions converts a User to UserUpdateOptions for use in UpdateUser @@ -107,7 +107,7 @@ func (i User) GetUpdateOptions() (o UserUpdateOptions) { o.Restricted = copyBool(&i.Restricted) o.Email = i.Email - return + return o } // ListUsers lists Users on the account diff --git a/alert_channels.go b/alert_channels.go new file mode 100644 index 000000000..0a7cc4224 --- /dev/null +++ b/alert_channels.go @@ -0,0 +1,72 @@ +package linodego + +import ( + "context" +) + +// 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 { + ID int `json:"id"` + Label string `json:"label"` + ChannelType string `json:"channel_type"` + Content ChannelContent `json:"content"` + Created string `json:"created"` + CreatedBy string `json:"created_by"` + Updated string `json:"updated"` + UpdatedBy string `json:"updated_by"` + URL string `json:"url"` +} + +// AlertChannelDetail represents the details of a Monitor Channel. +type AlertChannelDetail struct { + To string `json:"to,omitempty"` + From string `json:"from,omitempty"` + User string `json:"user,omitempty"` + Token string `json:"token,omitempty"` + URL string `json:"url,omitempty"` +} + +// AlertChannelCreateOptions are the options used to create a new Monitor Channel. +type AlertChannelCreateOptions struct { + Label string `json:"label"` + Type string `json:"type"` + Details AlertChannelDetailOptions `json:"details"` +} + +// AlertChannelDetailOptions are the options used to create the details of a new Monitor Channel. +type AlertChannelDetailOptions struct { + To string `json:"to,omitempty"` +} + +// Backwards-compat alias for older name +type AlertingChannelCreateOptions = AlertChannelCreateOptions + +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,omitempty"` + // Other channel types like 'webhook', 'slack' could be added here as optional fields. +} + +// ListAlertChannels gets a paginated list of Alert Channels. +func (c *Client) ListAlertChannels(ctx context.Context, opts *ListOptions) ([]AlertChannel, error) { + endpoint := formatAPIV4BetaPath("monitor/alert-channels") + return getPaginatedResults[AlertChannel](ctx, c, endpoint, opts) +} + +// GetAlertChannel gets an Alert Channel by ID. +func (c *Client) GetAlertChannel(ctx context.Context, channelID int) (*AlertChannel, error) { + e := formatAPIV4BetaPath("monitor/alert-channels/%d", channelID) + return doGETRequest[AlertChannel](ctx, c, e) +} diff --git a/betas.go b/betas.go index 97a89ef5b..476ef351f 100644 --- a/betas.go +++ b/betas.go @@ -8,7 +8,7 @@ import ( "github.com/linode/linodego/internal/parseabletime" ) -// Beta Program is a new product or service that is not generally available to all Akamai customers. +// BetaProgram is a new product or service that is not generally available to all Akamai customers. // Users must enroll into a beta in order to access the functionality. type BetaProgram struct { Label string `json:"label"` diff --git a/client.go b/client.go index 476151e48..bff9a0b78 100644 --- a/client.go +++ b/client.go @@ -45,7 +45,7 @@ const ( APIEnvVar = "LINODE_TOKEN" // APISecondsPerPoll how frequently to poll for new Events or Status in WaitFor functions APISecondsPerPoll = 3 - // Maximum wait time for retries + // APIRetryMaxWaitTime is the maximum wait time for retries APIRetryMaxWaitTime = time.Duration(30) * time.Second APIDefaultCacheExpiration = time.Minute * 15 ) @@ -171,7 +171,7 @@ func NewClient(hc *http.Client) (client Client) { SetDebug(envDebug). enableLogSanitization() - return + return client } // NewClientFromEnv creates a Client and initializes it with values diff --git a/client_monitor.go b/client_monitor.go index 33d42b161..b54dc8284 100644 --- a/client_monitor.go +++ b/client_monitor.go @@ -66,7 +66,7 @@ func NewMonitorClient(hc *http.Client) (mClient MonitorClient) { mClient.SetDebug(envDebug) - return + return mClient } // SetUserAgent sets a custom user-agent for HTTP requests diff --git a/domain_records.go b/domain_records.go index 373d797e7..2d8865e66 100644 --- a/domain_records.go +++ b/domain_records.go @@ -105,7 +105,7 @@ func (d DomainRecord) GetUpdateOptions() (du DomainRecordUpdateOptions) { du.TTLSec = d.TTLSec du.Tag = copyString(d.Tag) - return + return du } // ListDomainRecords lists DomainRecords diff --git a/domains.go b/domains.go index 7e04b4eed..4f2f2179b 100644 --- a/domains.go +++ b/domains.go @@ -190,7 +190,7 @@ func (d Domain) GetUpdateOptions() (du DomainUpdateOptions) { du.RefreshSec = d.RefreshSec du.TTLSec = d.TTLSec - return + return du } // ListDomains lists Domains diff --git a/firewall_devices.go b/firewall_devices.go index adbd9a17c..9b2dda688 100644 --- a/firewall_devices.go +++ b/firewall_devices.go @@ -13,9 +13,9 @@ type FirewallDeviceType string // FirewallDeviceType constants start with FirewallDevice const ( - FirewallDeviceLinode FirewallDeviceType = "linode" - FirewallDeviceNodeBalancer FirewallDeviceType = "nodebalancer" - FirewallDeviceInterface FirewallDeviceType = "interface" + FirewallDeviceLinode FirewallDeviceType = "linode" + FirewallDeviceNodeBalancer FirewallDeviceType = "nodebalancer" + FirewallDeviceLinodeInterface FirewallDeviceType = "linode_interface" ) // FirewallDevice represents a device governed by a Firewall @@ -74,7 +74,7 @@ func (c *Client) GetFirewallDevice(ctx context.Context, firewallID, deviceID int return doGETRequest[FirewallDevice](ctx, c, e) } -// AddFirewallDevice associates a Device with a given Firewall +// CreateFirewallDevice associates a Device with a given Firewall func (c *Client) CreateFirewallDevice(ctx context.Context, firewallID int, opts FirewallDeviceCreateOptions) (*FirewallDevice, error) { e := formatAPIPath("networking/firewalls/%d/devices", firewallID) return doPOSTRequest[FirewallDevice](ctx, c, e, opts) diff --git a/firewall_templates.go b/firewall_templates.go index e3be54fda..e2f5f6702 100644 --- a/firewall_templates.go +++ b/firewall_templates.go @@ -9,15 +9,15 @@ type FirewallTemplate struct { Rules FirewallRuleSet `json:"rules"` } -// NOTE: This feature may not currently be available to all users. // GetFirewallTemplate gets a FirewallTemplate given a slug. +// NOTE: This feature may not currently be available to all users. func (c *Client) GetFirewallTemplate(ctx context.Context, slug string) (*FirewallTemplate, error) { e := formatAPIPath("networking/firewalls/templates/%s", slug) return doGETRequest[FirewallTemplate](ctx, c, e) } -// NOTE: This feature may not currently be available to all users. // ListFirewallTemplates gets all available firewall templates for the account. +// NOTE: This feature may not currently be available to all users. func (c *Client) ListFirewallTemplates(ctx context.Context, opts *ListOptions) ([]FirewallTemplate, error) { return getPaginatedResults[FirewallTemplate](ctx, c, "networking/firewalls/templates", opts) } diff --git a/go.mod b/go.mod index c1f3fca7b..3d59b274d 100644 --- a/go.mod +++ b/go.mod @@ -5,21 +5,21 @@ require ( github.com/google/go-cmp v0.7.0 github.com/google/go-querystring v1.1.0 github.com/jarcoal/httpmock v1.4.1 - golang.org/x/net v0.43.0 - golang.org/x/oauth2 v0.30.0 - golang.org/x/text v0.28.0 + golang.org/x/net v0.44.0 + golang.org/x/oauth2 v0.31.0 + golang.org/x/text v0.29.0 gopkg.in/ini.v1 v1.66.6 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.11.0 + github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v3 v3.0.1 // indirect ) -go 1.23.0 +go 1.24.0 -toolchain go1.24.1 +toolchain go1.25.1 retract v1.0.0 // Accidental branch push diff --git a/go.sum b/go.sum index ad2be32b6..9c0b66587 100644 --- a/go.sum +++ b/go.sum @@ -13,14 +13,14 @@ github.com/maxatome/go-testdeep v1.14.0 h1:rRlLv1+kI8eOI3OaBXZwb3O7xY3exRzdW5QyX github.com/maxatome/go-testdeep v1.14.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= -github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= +golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/go.work b/go.work index f6e93a8a2..bc644fc85 100644 --- a/go.work +++ b/go.work @@ -1,6 +1,6 @@ -go 1.23.0 +go 1.24.0 -toolchain go1.23.4 +toolchain go1.25.1 use ( . diff --git a/images.go b/images.go index 858136a41..21b2853fb 100644 --- a/images.go +++ b/images.go @@ -142,7 +142,7 @@ func (i Image) GetUpdateOptions() (iu ImageUpdateOptions) { iu.Label = i.Label iu.Description = copyString(&i.Description) - return + return iu } // ListImages lists Images. diff --git a/instance_config_interfaces.go b/instance_config_interfaces.go index e7454618f..c1c8735f8 100644 --- a/instance_config_interfaces.go +++ b/instance_config_interfaces.go @@ -27,7 +27,7 @@ type InstanceConfigInterface struct { type InstanceConfigInterfaceIPv6 struct { SLAAC []InstanceConfigInterfaceIPv6SLAAC `json:"slaac"` Ranges []InstanceConfigInterfaceIPv6Range `json:"ranges"` - IsPublic bool `json:"is_public"` + IsPublic *bool `json:"is_public"` } // InstanceConfigInterfaceIPv6SLAAC represents a single IPv6 SLAAC under @@ -173,7 +173,7 @@ func (i InstanceConfigInterface) GetCreateOptions() InstanceConfigInterfaceCreat } }, ), - IsPublic: copyValue(&ipv6.IsPublic), + IsPublic: copyValue(ipv6.IsPublic), } } @@ -219,7 +219,7 @@ func (i InstanceConfigInterface) GetUpdateOptions() InstanceConfigInterfaceUpdat opts.IPv6 = &InstanceConfigInterfaceUpdateOptionsIPv6{ SLAAC: &newSLAAC, Ranges: &newRanges, - IsPublic: copyValue(&ipv6.IsPublic), + IsPublic: copyValue(ipv6.IsPublic), } } } diff --git a/instance_ips.go b/instance_ips.go index cd87dbcd7..cd2a78505 100644 --- a/instance_ips.go +++ b/instance_ips.go @@ -145,7 +145,7 @@ func (c *Client) DeleteInstanceIPAddress(ctx context.Context, linodeID int, ipAd return doDELETERequest(ctx, c, e) } -// Function to add additional reserved IPV4 addresses to an existing linode +// AssignInstanceReservedIP adds additional reserved IPV4 addresses to an existing linode func (c *Client) AssignInstanceReservedIP(ctx context.Context, linodeID int, opts InstanceReserveIPOptions) (*InstanceIP, error) { endpoint := formatAPIPath("linode/instances/%d/ips", linodeID) return doPOSTRequest[InstanceIP](ctx, c, endpoint, opts) diff --git a/interfaces.go b/interfaces.go index b10e6cdfe..d91d45e2a 100644 --- a/interfaces.go +++ b/interfaces.go @@ -90,7 +90,7 @@ type VPCInterfaceIPv4Range struct { type VPCInterfaceIPv6 struct { SLAAC []VPCInterfaceIPv6SLAAC `json:"slaac"` Ranges []VPCInterfaceIPv6Range `json:"ranges"` - IsPublic bool `json:"is_public"` + IsPublic *bool `json:"is_public"` } // VPCInterfaceIPv6SLAAC contains the information for a single IPv6 SLAAC under a VPC. @@ -122,8 +122,7 @@ type LinodeInterfaceCreateOptions struct { type LinodeInterfaceUpdateOptions struct { DefaultRoute *InterfaceDefaultRoute `json:"default_route,omitempty"` Public *PublicInterfaceCreateOptions `json:"public,omitempty"` - VPC *VPCInterfaceCreateOptions `json:"vpc,omitempty"` - VLAN *VLANInterface `json:"vlan,omitempty"` + VPC *VPCInterfaceUpdateOptions `json:"vpc,omitempty"` } type PublicInterfaceCreateOptions struct { @@ -132,7 +131,7 @@ type PublicInterfaceCreateOptions struct { } type PublicInterfaceIPv4CreateOptions struct { - Addresses []PublicInterfaceIPv4AddressCreateOptions `json:"addresses,omitempty"` + Addresses *[]PublicInterfaceIPv4AddressCreateOptions `json:"addresses,omitempty"` } type PublicInterfaceIPv4AddressCreateOptions struct { @@ -141,7 +140,7 @@ type PublicInterfaceIPv4AddressCreateOptions struct { } type PublicInterfaceIPv6CreateOptions struct { - Ranges []PublicInterfaceIPv6RangeCreateOptions `json:"ranges,omitempty"` + Ranges *[]PublicInterfaceIPv6RangeCreateOptions `json:"ranges,omitempty"` } type PublicInterfaceIPv6RangeCreateOptions struct { @@ -155,8 +154,8 @@ type VPCInterfaceCreateOptions struct { } type VPCInterfaceIPv4CreateOptions struct { - Addresses []VPCInterfaceIPv4AddressCreateOptions `json:"addresses,omitempty"` - Ranges []VPCInterfaceIPv4RangeCreateOptions `json:"ranges,omitempty"` + Addresses *[]VPCInterfaceIPv4AddressCreateOptions `json:"addresses,omitempty"` + Ranges *[]VPCInterfaceIPv4RangeCreateOptions `json:"ranges,omitempty"` } type VPCInterfaceIPv4AddressCreateOptions struct { @@ -172,9 +171,9 @@ type VPCInterfaceIPv4RangeCreateOptions struct { // VPCInterfaceIPv6CreateOptions specifies IPv6 configuration parameters for VPC creation. // NOTE: IPv6 interfaces may not currently be available to all users. type VPCInterfaceIPv6CreateOptions struct { - SLAAC []VPCInterfaceIPv6SLAACCreateOptions `json:"slaac,omitempty"` - Ranges []VPCInterfaceIPv6RangeCreateOptions `json:"ranges,omitempty"` - IsPublic bool `json:"is_public"` + SLAAC *[]VPCInterfaceIPv6SLAACCreateOptions `json:"slaac,omitempty"` + Ranges *[]VPCInterfaceIPv6RangeCreateOptions `json:"ranges,omitempty"` + IsPublic *bool `json:"is_public"` } // VPCInterfaceIPv6SLAACCreateOptions defines the IPv6 SLAAC configuration parameters for VPC creation. @@ -189,6 +188,11 @@ type VPCInterfaceIPv6RangeCreateOptions struct { Range string `json:"range"` } +type VPCInterfaceUpdateOptions struct { + IPv4 *VPCInterfaceIPv4CreateOptions `json:"ipv4,omitempty"` + IPv6 *VPCInterfaceIPv6CreateOptions `json:"ipv6,omitempty"` +} + type LinodeInterfacesUpgrade struct { ConfigID int `json:"config_id"` DryRun bool `json:"dry_run"` diff --git a/k8s/go.mod b/k8s/go.mod index 84151a16d..f52560b52 100644 --- a/k8s/go.mod +++ b/k8s/go.mod @@ -29,11 +29,11 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/net v0.44.0 // indirect + golang.org/x/oauth2 v0.31.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.35.0 // indirect + golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.6.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -50,6 +50,4 @@ require ( replace github.com/linode/linodego => ../ -go 1.23.0 - -toolchain go1.23.4 +go 1.24.0 diff --git a/k8s/go.sum b/k8s/go.sum index c81aea95c..1a3e31e29 100644 --- a/k8s/go.sum +++ b/k8s/go.sum @@ -78,8 +78,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= -github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -91,32 +91,32 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= +golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/lke_clusters.go b/lke_clusters.go index 2b6b1d4bc..786b6faaf 100644 --- a/lke_clusters.go +++ b/lke_clusters.go @@ -18,6 +18,13 @@ const ( LKEClusterNotReady LKEClusterStatus = "not_ready" ) +type LKEClusterStackType string + +const ( + LKEClusterStackIPv4 LKEClusterStackType = "ipv4" + LKEClusterDualStack LKEClusterStackType = "ipv4-ipv6" +) + // LKECluster represents a LKECluster object type LKECluster struct { ID int `json:"id"` @@ -35,6 +42,11 @@ type LKECluster struct { // NOTE: APLEnabled is currently in beta and may only function with API version v4beta. APLEnabled bool `json:"apl_enabled"` + + // NOTE: SubnetID, VpcID, and StackType may not currently be available to all users and can only be used with v4beta. + SubnetID int `json:"subnet_id"` + VpcID int `json:"vpc_id"` + StackType LKEClusterStackType `json:"stack_type"` } // LKEClusterCreateOptions fields are those accepted by CreateLKECluster @@ -51,6 +63,11 @@ type LKEClusterCreateOptions struct { // NOTE: APLEnabled is currently in beta and may only function with API version v4beta. APLEnabled bool `json:"apl_enabled,omitempty"` + + // NOTE: SubnetID, VpcID, and StackType may not currently be available to all users and can only be used with v4beta. + SubnetID *int `json:"subnet_id,omitempty"` + VpcID *int `json:"vpc_id,omitempty"` + StackType *LKEClusterStackType `json:"stack_type,omitempty"` } // LKEClusterUpdateOptions fields are those accepted by UpdateLKECluster @@ -141,7 +158,7 @@ func (i LKECluster) GetCreateOptions() (o LKEClusterCreateOptions) { } // @TODO copy NodePools? - return + return o } // GetUpdateOptions converts a LKECluster to LKEClusterUpdateOptions for use in UpdateLKECluster @@ -157,7 +174,7 @@ func (i LKECluster) GetUpdateOptions() (o LKEClusterUpdateOptions) { // ACL will not be populated in the control plane response } - return + return o } // ListLKEVersions lists the Kubernetes versions available through LKE. This endpoint is cached by default. diff --git a/lke_clusters_control_plane.go b/lke_clusters_control_plane.go index 75b4d28c7..5a3544a25 100644 --- a/lke_clusters_control_plane.go +++ b/lke_clusters_control_plane.go @@ -5,6 +5,9 @@ import "context" // LKEClusterControlPlane fields contained within the `control_plane` attribute of an LKE cluster. type LKEClusterControlPlane struct { HighAvailability bool `json:"high_availability"` + + // AuditLogsEnabled may not currently be available to all users and can only be used with v4beta. + AuditLogsEnabled bool `json:"audit_logs_enabled,omitempty"` } // LKEClusterControlPlaneACLAddresses describes the @@ -42,6 +45,9 @@ type LKEClusterControlPlaneACLOptions struct { type LKEClusterControlPlaneOptions struct { HighAvailability *bool `json:"high_availability,omitempty"` ACL *LKEClusterControlPlaneACLOptions `json:"acl,omitempty"` + + // AuditLogsEnabled may not currently be available to all users and can only be used with v4beta. + AuditLogsEnabled *bool `json:"audit_logs_enabled,omitempty"` } // LKEClusterControlPlaneACLUpdateOptions represents the options diff --git a/lke_node_pools.go b/lke_node_pools.go index 62cce2ced..fc90ed25d 100644 --- a/lke_node_pools.go +++ b/lke_node_pools.go @@ -132,7 +132,7 @@ func (l LKENodePool) GetCreateOptions() (o LKENodePoolCreateOptions) { o.UpdateStrategy = l.UpdateStrategy o.Label = l.Label - return + return o } // GetUpdateOptions converts a LKENodePool to LKENodePoolUpdateOptions for use in UpdateLKENodePoolUpdate @@ -146,7 +146,7 @@ func (l LKENodePool) GetUpdateOptions() (o LKENodePoolUpdateOptions) { o.UpdateStrategy = l.UpdateStrategy o.Label = l.Label - return + return o } // ListLKENodePools lists LKENodePools diff --git a/longview.go b/longview.go index 14d8fb41b..16be227e3 100644 --- a/longview.go +++ b/longview.go @@ -28,7 +28,7 @@ type LongviewClientCreateOptions struct { Label string `json:"label"` } -// LongviewClientCreateOptions is an options struct used when Updating a Longview Client +// LongviewClientUpdateOptions is an options struct used when Updating a Longview Client type LongviewClientUpdateOptions struct { Label string `json:"label"` } diff --git a/monitor_alert_definitions.go b/monitor_alert_definitions.go new file mode 100644 index 000000000..9e3339234 --- /dev/null +++ b/monitor_alert_definitions.go @@ -0,0 +1,201 @@ +package linodego + +import ( + "context" + "encoding/json" + "time" + + "github.com/linode/linodego/internal/parseabletime" +) + +// AlertDefinition represents an ACLP Alert Definition object +type AlertDefinition struct { + ID int `json:"id"` + Label string `json:"label"` + Severity int `json:"severity"` + Type string `json:"type"` + ServiceType string `json:"service_type"` + Status string `json:"status"` + HasMoreResources bool `json:"has_more_resources"` + Rule *Rule `json:"rule"` + RuleCriteria *RuleCriteria `json:"rule_criteria"` + TriggerConditions *TriggerConditions `json:"trigger_conditions"` + AlertChannels []AlertChannelEnvelope `json:"alert_channels"` + Created *time.Time `json:"-"` + Updated *time.Time `json:"-"` + UpdatedBy string `json:"updated_by"` + CreatedBy string `json:"created_by"` + EntityIDs []string `json:"entity_ids"` + Description string `json:"description"` + Class string `json:"class"` +} + +// Backwards-compatible alias +type MonitorAlertDefinition = AlertDefinition + +// TriggerConditions represents the trigger conditions for an alert. +type TriggerConditions struct { + CriteriaCondition string `json:"criteria_condition,omitempty"` + EvaluationPeriodSeconds int `json:"evaluation_period_seconds,omitempty"` + PollingIntervalSeconds int `json:"polling_interval_seconds,omitempty"` + TriggerOccurrences int `json:"trigger_occurrences,omitempty"` +} + +// RuleCriteria represents the rule criteria for an alert. +type RuleCriteria struct { + Rules []Rule `json:"rules,omitempty"` +} + +// Rule represents a single rule for an alert. +type Rule struct { + AggregateFunction string `json:"aggregate_function,omitempty"` + DimensionFilters []DimensionFilter `json:"dimension_filters,omitempty"` + Label string `json:"label,omitempty"` + Metric string `json:"metric,omitempty"` + Operator string `json:"operator,omitempty"` + Threshold *float64 `json:"threshold,omitempty"` + Unit *string `json:"unit,omitempty"` +} + +// DimensionFilter represents a single dimension filter used inside a Rule. +type DimensionFilter struct { + DimensionLabel string `json:"dimension_label"` + Label string `json:"label"` + Operator string `json:"operator"` + Value interface{} `json:"value"` +} + +// AlertType represents the type of alert: "user" or "system" +type AlertType string + +const ( + AlertTypeUser AlertType = "user" + AlertTypeSystem AlertType = "system" +) + +// Severity represents the severity level of an alert. +// 0 = Severe, 1 = Medium, 2 = Low, 3 = Info +type Severity int + +const ( + SeveritySevere Severity = 0 + SeverityMedium Severity = 1 + SeverityLow Severity = 2 + SeverityInfo Severity = 3 +) + +// CriteriaCondition represents supported criteria conditions +type CriteriaCondition string + +const ( + CriteriaConditionAll CriteriaCondition = "ALL" +) + +// AlertDefinitionCreateOptions are the options used to create a new alert definition. +type AlertDefinitionCreateOptions struct { + ServiceType string `json:"service_type"` // mandatory + Label string `json:"label"` // mandatory + Severity int `json:"severity"` // mandatory + ChannelIDs []int `json:"channel_ids"` // mandatory + RuleCriteria *RuleCriteria `json:"rule_criteria,omitempty"` // optional + TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` // optional + EntityIDs []string `json:"entity_ids,omitempty"` // optional + Description string `json:"description,omitempty"` // optional +} + +// AlertDefinitionUpdateOptions are the options used to update an alert definition. +type AlertDefinitionUpdateOptions struct { + ServiceType string `json:"service_type"` // mandatory, must not be empty + AlertID int `json:"alert_id"` // mandatory, must not be zero + Label string `json:"label,omitempty"` // optional + Severity int `json:"severity,omitempty"` // optional, should be int to match AlertDefinition + Description string `json:"description,omitempty"` // optional + RuleCriteria *RuleCriteria `json:"rule_criteria,omitempty"` // optional + TriggerConditions *TriggerConditions `json:"trigger_conditions,omitempty"` // optional + EntityIDs []string `json:"entity_ids,omitempty"` // optional + ChannelIDs []int `json:"channel_ids,omitempty"` // optional +} + +// UnmarshalJSON implements the json.Unmarshaler interface +func (i *AlertDefinition) UnmarshalJSON(b []byte) error { + type Mask AlertDefinition + + p := struct { + *Mask + Created *parseabletime.ParseableTime `json:"created"` + Updated *parseabletime.ParseableTime `json:"updated"` + }{ + Mask: (*Mask)(i), + } + + if err := json.Unmarshal(b, &p); err != nil { + return err + } + + i.Created = (*time.Time)(p.Created) + i.Updated = (*time.Time)(p.Updated) + + return nil +} + +// ListMonitorAlertDefinitions gets a paginated list of ACLP Monitor Alert Definitions. +func (c *Client) ListMonitorAlertDefinitions(ctx context.Context, serviceType string, opts *ListOptions) ([]MonitorAlertDefinition, error) { + var endpoint string + if serviceType != "" { + endpoint = formatAPIV4BetaPath("monitor/services/%s/alert-definitions", serviceType) + } else { + endpoint = formatAPIV4BetaPath("monitor/alert-definitions") + } + return getPaginatedResults[AlertDefinition](ctx, c, endpoint, opts) +} + +// GetMonitorAlertDefinition gets an ACLP Monitor Alert Definition. +func (c *Client) GetMonitorAlertDefinition(ctx context.Context, serviceType string, alertID int) (*MonitorAlertDefinition, error) { + e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions/%d", serviceType, alertID) + return doGETRequest[AlertDefinition](ctx, c, e) +} + +// CreateMonitorAlertDefinition creates an ACLP Monitor Alert Definition. +func (c *Client) CreateMonitorAlertDefinition(ctx context.Context, serviceType string, opts AlertDefinitionCreateOptions) (*MonitorAlertDefinition, error) { + e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions", serviceType) + return doPOSTRequest[AlertDefinition](ctx, c, e, opts) +} + +// CreateMonitorAlertDefinitionWithIdempotency creates an ACLP Monitor Alert Definition +// and optionally sends an Idempotency-Key header to make the request idempotent. +func (c *Client) CreateMonitorAlertDefinitionWithIdempotency(ctx context.Context, serviceType string, opts AlertDefinitionCreateOptions, idempotencyKey string) (*MonitorAlertDefinition, error) { + e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions", serviceType) + + var result AlertDefinition + req := c.R(ctx).SetResult(&result) + + if idempotencyKey != "" { + req.SetHeader("Idempotency-Key", idempotencyKey) + } + + body, err := json.Marshal(opts) + if err != nil { + return nil, err + } + + req.SetBody(string(body)) + + r, err := coupleAPIErrors(req.Post(e)) + if err != nil { + return nil, err + } + + return r.Result().(*AlertDefinition), nil +} + +// UpdateMonitorAlertDefinition updates an ACLP Monitor Alert Definition. +func (c *Client) UpdateMonitorAlertDefinition(ctx context.Context, serviceType string, alertID int, opts AlertDefinitionUpdateOptions) (*AlertDefinition, error) { + e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions/%d", serviceType, alertID) + return doPUTRequest[AlertDefinition](ctx, c, e, opts) +} + +// DeleteMonitorAlertDefinition deletes an ACLP Monitor Alert Definition. +func (c *Client) DeleteMonitorAlertDefinition(ctx context.Context, serviceType string, alertID int) error { + e := formatAPIV4BetaPath("monitor/services/%s/alert-definitions/%d", serviceType, alertID) + return doDELETERequest(ctx, c, e) +} diff --git a/monitor_dashboards.go b/monitor_dashboards.go index 2554a7b59..18d8d7ada 100644 --- a/monitor_dashboards.go +++ b/monitor_dashboards.go @@ -19,7 +19,7 @@ type MonitorDashboard struct { Widgets []DashboardWidget `json:"widgets"` } -// enum object for serviceType +// ServiceType is an enum object for serviceType type ServiceType string const ( @@ -28,12 +28,12 @@ const ( ServiceTypeDBaaS ServiceType = "dbaas" ServiceTypeACLB ServiceType = "aclb" ServiceTypeNodeBalancer ServiceType = "nodebalancer" - ServiceTypeObjectStorage ServiceType = "objectstorage" + ServiceTypeObjectStorage ServiceType = "object_storage" ServiceTypeVPC ServiceType = "vpc" ServiceTypeFirewallService ServiceType = "firewall" ) -// enum object for DashboardType +// DashboardType is an enum object for DashboardType type DashboardType string const ( @@ -53,7 +53,7 @@ type DashboardWidget struct { AggregateFunction AggregateFunction `json:"aggregate_function"` } -// Enum object for AggregateFunction +// AggregateFunction is an enum object for AggregateFunction type AggregateFunction string const ( @@ -67,7 +67,7 @@ const ( AggregateFunctionLast AggregateFunction = "last" ) -// Enum object for Chart type +// ChartType is an enum object for Chart type type ChartType string const ( diff --git a/monitor_metrics_definitions.go b/monitor_metrics_definitions.go index eca52b112..026652a00 100644 --- a/monitor_metrics_definitions.go +++ b/monitor_metrics_definitions.go @@ -16,7 +16,7 @@ type MonitorMetricsDefinition struct { Unit MetricUnit `json:"unit"` } -// Enum object for MetricType +// MetricType is an enum object for MetricType type MetricType string const ( @@ -26,7 +26,7 @@ const ( MetricTypeSummary MetricType = "summary" ) -// Enum object for Unit +// MetricUnit is an enum object for Unit type MetricUnit string const ( diff --git a/monitor_services_create_token.go b/monitor_services_create_token.go index a5cb47ae9..a4c679e05 100644 --- a/monitor_services_create_token.go +++ b/monitor_services_create_token.go @@ -9,7 +9,7 @@ type MonitorServiceToken struct { Token string `json:"token"` } -// Create token options. +// MonitorTokenCreateOptions contains create token options. type MonitorTokenCreateOptions struct { // EntityIDs are expected to be type "any" as different service_types have different variable type for their entity_ids. For example, Linode has "int" entity_ids whereas object storage has "string" as entity_ids. EntityIDs []any `json:"entity_ids"` diff --git a/network_ips.go b/network_ips.go index 8cebabb42..72be24c63 100644 --- a/network_ips.go +++ b/network_ips.go @@ -69,7 +69,7 @@ func (i InstanceIP) GetUpdateOptionsV2() IPAddressUpdateOptionsV2 { // Deprecated: Please use GetUpdateOptionsV2 for all new implementations. func (i InstanceIP) GetUpdateOptions() (o IPAddressUpdateOptions) { o.RDNS = copyString(&i.RDNS) - return + return o } // ListIPAddresses lists IPAddresses. diff --git a/object_storage_bucket_certs.go b/object_storage_bucket_certs.go index 6597e40f5..4f52859ca 100644 --- a/object_storage_bucket_certs.go +++ b/object_storage_bucket_certs.go @@ -36,7 +36,7 @@ func (c *Client) GetObjectStorageBucketCert(ctx context.Context, clusterOrRegion return doGETRequest[ObjectStorageBucketCert](ctx, c, e) } -// UploadObjectStorageBucketCert uploads a TLS/SSL Cert to be used with an Object Storage Bucket. +// UploadObjectStorageBucketCertV2 uploads a TLS/SSL Cert to be used with an Object Storage Bucket. func (c *Client) UploadObjectStorageBucketCertV2( ctx context.Context, clusterOrRegionID, bucket string, diff --git a/object_storage_buckets.go b/object_storage_buckets.go index 8755f3ade..bdcb54229 100644 --- a/object_storage_buckets.go +++ b/object_storage_buckets.go @@ -159,7 +159,7 @@ func (c *Client) UpdateObjectStorageBucketAccess(ctx context.Context, clusterOrR return doPOSTRequestNoResponseBody(ctx, c, e, opts) } -// GetObjectStorageBucketAccess gets the current access config for a bucket +// GetObjectStorageBucketAccessV2 gets the current access config for a bucket func (c *Client) GetObjectStorageBucketAccessV2(ctx context.Context, clusterOrRegionID, label string) (*ObjectStorageBucketAccessV2, error) { e := formatAPIPath("object-storage/buckets/%s/%s/access", clusterOrRegionID, label) return doGETRequest[ObjectStorageBucketAccessV2](ctx, c, e) @@ -171,7 +171,7 @@ func (c *Client) DeleteObjectStorageBucket(ctx context.Context, clusterOrRegionI return doDELETERequest(ctx, c, e) } -// Lists the contents of the specified ObjectStorageBucket +// ListObjectStorageBucketContents lists the contents of the specified ObjectStorageBucket func (c *Client) ListObjectStorageBucketContents( ctx context.Context, clusterOrRegionID, label string, diff --git a/object_storage_endpoints.go b/object_storage_endpoints.go index 635091948..f3ad166c6 100644 --- a/object_storage_endpoints.go +++ b/object_storage_endpoints.go @@ -2,7 +2,7 @@ package linodego import "context" -// NotificationType constants start with Notification and include all known Linode API Notification Types. +// ObjectStorageEndpointType constants start with Notification and include all known Linode API Notification Types. type ObjectStorageEndpointType string // NotificationType constants represent the actions that cause a Notification. New types may be added in the future. diff --git a/profile.go b/profile.go index ec227b6d1..c290d62b5 100644 --- a/profile.go +++ b/profile.go @@ -66,7 +66,7 @@ func (i Profile) GetUpdateOptions() (o ProfileUpdateOptions) { o.TwoFactorAuth = copyBool(&i.TwoFactorAuth) o.Restricted = copyBool(&i.Restricted) - return + return o } // GetProfile returns the Profile of the authenticated user diff --git a/profile_logins.go b/profile_logins.go index 76f0f201f..96740a398 100644 --- a/profile_logins.go +++ b/profile_logins.go @@ -8,7 +8,7 @@ import ( "github.com/linode/linodego/internal/parseabletime" ) -// Profile represents a Profile object +// ProfileLogin represents a Profile object type ProfileLogin struct { Datetime *time.Time `json:"datetime"` ID int `json:"id"` diff --git a/profile_sshkeys.go b/profile_sshkeys.go index 30b0e4bfe..a18566fdb 100644 --- a/profile_sshkeys.go +++ b/profile_sshkeys.go @@ -53,13 +53,13 @@ func (i SSHKey) GetCreateOptions() (o SSHKeyCreateOptions) { o.Label = i.Label o.SSHKey = i.SSHKey - return + return o } // GetUpdateOptions converts a SSHKey to SSHKeyCreateOptions for use in UpdateSSHKey func (i SSHKey) GetUpdateOptions() (o SSHKeyUpdateOptions) { o.Label = i.Label - return + return o } // ListSSHKeys lists SSHKeys diff --git a/profile_tokens.go b/profile_tokens.go index 958bedaec..0622ec1d3 100644 --- a/profile_tokens.go +++ b/profile_tokens.go @@ -77,13 +77,13 @@ func (i Token) GetCreateOptions() (o TokenCreateOptions) { o.Expiry = copyTime(i.Expiry) o.Scopes = i.Scopes - return + return o } // GetUpdateOptions converts a Token to TokenUpdateOptions for use in UpdateToken func (i Token) GetUpdateOptions() (o TokenUpdateOptions) { o.Label = i.Label - return + return o } // ListTokens lists Tokens diff --git a/regions_availability.go b/regions_availability.go index 4caefeb74..dcba07f75 100644 --- a/regions_availability.go +++ b/regions_availability.go @@ -4,7 +4,7 @@ import ( "context" ) -// Region represents a linode region object +// RegionAvailability represents a linode region object type RegionAvailability struct { Region string `json:"region"` Plan string `json:"plan"` diff --git a/request_helpers.go b/request_helpers.go index 15adcaa01..0cddc9519 100644 --- a/request_helpers.go +++ b/request_helpers.go @@ -6,6 +6,7 @@ import ( "fmt" "net/url" "reflect" + "strings" ) // paginatedResponse represents a single response from a paginated @@ -297,6 +298,17 @@ func formatAPIPath(format string, args ...any) string { return fmt.Sprintf(format, escapedArgs...) } +// formatAPIV4BetaPath builds a fully-qualified URL for v4beta endpoints. +// We return a full URL (including scheme and host) so requests made with the +// standard client (which is pointed at /v4) will hit the /v4beta host/path +// directly. +func formatAPIV4BetaPath(format string, args ...any) string { + p := formatAPIPath(format, args...) + // Ensure we don't produce a double slash when joining + p = strings.TrimPrefix(p, "/") + return fmt.Sprintf("%s://%s/%s/%s", APIProto, APIHost, "v4beta", p) +} + func isNil(i interface{}) bool { if i == nil { return true diff --git a/retries.go b/retries.go index 8831b5200..047fed84e 100644 --- a/retries.go +++ b/retries.go @@ -18,10 +18,10 @@ const ( defaultRetryCount = 1000 ) -// type RetryConditional func(r *resty.Response) (shouldRetry bool) +// RetryConditional func(r *resty.Response) (shouldRetry bool) type RetryConditional resty.RetryConditionFunc -// type RetryAfter func(c *resty.Client, r *resty.Response) (time.Duration, error) +// RetryAfter func(c *resty.Client, r *resty.Response) (time.Duration, error) type RetryAfter resty.RetryAfterFunc // Configures resty to diff --git a/tags.go b/tags.go index 2d37ca219..e23ba2168 100644 --- a/tags.go +++ b/tags.go @@ -47,7 +47,7 @@ type TagCreateOptions struct { // GetCreateOptions converts a Tag to TagCreateOptions for use in CreateTag func (i Tag) GetCreateOptions() (o TagCreateOptions) { o.Label = i.Label - return + return o } // ListTags lists Tags diff --git a/test/go.mod b/test/go.mod index d97add10d..1d180d956 100644 --- a/test/go.mod +++ b/test/go.mod @@ -6,10 +6,10 @@ require ( github.com/jarcoal/httpmock v1.4.1 github.com/linode/linodego v1.33.0 github.com/linode/linodego/k8s v0.0.0-00010101000000-000000000000 - github.com/stretchr/testify v1.11.0 + github.com/stretchr/testify v1.11.1 golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 - golang.org/x/net v0.43.0 - golang.org/x/oauth2 v0.30.0 + golang.org/x/net v0.44.0 + golang.org/x/oauth2 v0.31.0 k8s.io/client-go v0.29.4 ) @@ -37,9 +37,9 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/term v0.35.0 // indirect + golang.org/x/text v0.29.0 // indirect golang.org/x/time v0.6.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -56,9 +56,7 @@ require ( sigs.k8s.io/yaml v1.3.0 // indirect ) -go 1.23.0 - -toolchain go1.23.4 +go 1.24.0 replace github.com/linode/linodego => ../ diff --git a/test/go.sum b/test/go.sum index 4ad57de1c..50464214c 100644 --- a/test/go.sum +++ b/test/go.sum @@ -85,8 +85,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= -github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -100,32 +100,32 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= +golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/test/integration/account_user_grants_test.go b/test/integration/account_user_grants_test.go index 45cdd0dae..32f4ce698 100644 --- a/test/integration/account_user_grants_test.go +++ b/test/integration/account_user_grants_test.go @@ -23,19 +23,18 @@ func TestUserGrants_Update(t *testing.T) { accessLevel := linodego.AccessLevelReadOnly globalGrants := linodego.GlobalUserGrants{ - AccountAccess: &accessLevel, - AddDomains: false, - AddDatabases: true, - AddFirewalls: true, - AddImages: true, - AddLinodes: false, - AddLongview: true, - AddNodeBalancers: false, - AddPlacementGroups: false, - AddStackScripts: true, - AddVolumes: true, - AddVPCs: true, - CancelAccount: false, + AccountAccess: &accessLevel, + AddDomains: false, + AddDatabases: true, + AddFirewalls: true, + AddImages: true, + AddLinodes: false, + AddLongview: true, + AddNodeBalancers: false, + AddStackScripts: true, + AddVolumes: true, + AddVPCs: true, + CancelAccount: false, } grants, err := client.UpdateUserGrants(context.TODO(), username, linodego.UserGrantsUpdateOptions{ diff --git a/test/integration/fixtures/TestInstance_GetMonthlyTransfer.yaml b/test/integration/fixtures/TestInstance_GetMonthlyTransfer.yaml index 847c771be..935c8a962 100644 --- a/test/integration/fixtures/TestInstance_GetMonthlyTransfer.yaml +++ b/test/integration/fixtures/TestInstance_GetMonthlyTransfer.yaml @@ -14,308 +14,382 @@ interactions: url: https://api.linode.com/v4beta/regions?page=1 method: GET response: - body: '{"data": [{"id": "ap-west", "label": "Mumbai, IN", "country": "in", "capabilities": - ["Linodes", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", - "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Linode - Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed - Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.105.34.5,172.105.35.5,172.105.36.5,172.105.37.5,172.105.38.5,172.105.39.5,172.105.40.5,172.105.41.5,172.105.42.5,172.105.43.5", + body: '{"data": [{"id": "mx-qro-1", "label": "Quer\u00e9taro, MX", "country": + "mx", "capabilities": ["Linodes", "Disk Encryption", "Cloud Firewall", "Vlans", + "VPCs", "Metadata", "Distributed Plans"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", + "ipv6": "1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + 0, "maximum_linodes_per_pg": 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": + "distributed"}, {"id": "us-mia", "label": "Miami, FL", "country": "us", "capabilities": + ["Linodes", "Block Storage Encryption", "LA Disk Encryption", "Disk Encryption", + "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", + "Kubernetes Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", + "Metadata", "Premium Plans", "Placement Group", "StackScripts", "NETINT Quadra + T1U", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.233.160.34,172.233.160.27,172.233.160.30,172.233.160.29,172.233.160.32,172.233.160.28,172.233.160.33,172.233.160.26,172.233.160.25,172.233.160.31", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ca-central", - "label": "Toronto, CA", "country": "ca", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement - Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed - Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.105.0.5,172.105.3.5,172.105.4.5,172.105.5.5,172.105.6.5,172.105.7.5,172.105.8.5,172.105.9.5,172.105.10.5,172.105.11.5", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "id-cgk", + "label": "Jakarta, ID", "country": "id", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.232.224.23,172.232.224.32,172.232.224.26,172.232.224.27,172.232.224.21,172.232.224.24,172.232.224.22,172.232.224.20,172.232.224.31,172.232.224.28", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-southeast", - "label": "Sydney, AU", "country": "au", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement - Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed - Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.105.166.5,172.105.169.5,172.105.168.5,172.105.172.5,172.105.162.5,172.105.170.5,172.105.167.5,172.105.171.5,172.105.181.5,172.105.161.5", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-lax", + "label": "Los Angeles, CA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "NETINT Quadra T1U", "Maintenance Policy", + "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.233.128.45,172.233.128.38,172.233.128.53,172.233.128.37,172.233.128.34,172.233.128.36,172.233.128.33,172.233.128.39,172.233.128.43,172.233.128.44", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-iad", - "label": "Washington, DC", "country": "us", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "139.144.192.62,139.144.192.60,139.144.192.61,139.144.192.53,139.144.192.54,139.144.192.67,139.144.192.69,139.144.192.66,139.144.192.52,139.144.192.68", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "nz-akl-1", + "label": "Auckland, NZ", "country": "nz", "capabilities": ["Linodes", "Disk + Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed Plans"], + "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": + "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "us-den-1", "label": "Denver, CO", "country": "us", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "de-ham-1", "label": "Hamburg, DE", "country": "de", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "fr-mrs-1", "label": "Marseille, FR", "country": "fr", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "za-jnb-1", "label": "Johannesburg, ZA", "country": "za", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "my-kul-1", "label": "Kuala Lumpur, MY", "country": "my", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "co-bog-1", "label": "Bogot\u00e1, CO", "country": "co", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "it-mil", "label": "Milan, IT", "country": "it", "capabilities": ["Linodes", + "Block Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", + "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.192.19,172.232.192.18,172.232.192.16,172.232.192.20,172.232.192.24,172.232.192.21,172.232.192.22,172.232.192.17,172.232.192.15,172.232.192.23", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-ord", - "label": "Chicago, IL", "country": "us", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud - Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", - "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": - ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.232.0.17,172.232.0.16,172.232.0.21,172.232.0.13,172.232.0.22,172.232.0.9,172.232.0.19,172.232.0.20,172.232.0.15,172.232.0.18", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-hou-1", + "label": "Houston, TX", "country": "us", "capabilities": ["Linodes", "Disk Encryption", + "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed Plans"], "monitors": + {"alerts": [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", + "ipv6": "1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + 0, "maximum_linodes_per_pg": 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": + "distributed"}, {"id": "cl-scl-1", "label": "Santiago, CL", "country": "cl", + "capabilities": ["Linodes", "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", + "Metadata", "Distributed Plans"], "monitors": {"alerts": [], "metrics": []}, + "status": "ok", "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": + "1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + 0, "maximum_linodes_per_pg": 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": + "distributed"}, {"id": "gb-lon", "label": "London 2, UK", "country": "gb", "capabilities": + ["Linodes", "Block Storage Encryption", "LA Disk Encryption", "Disk Encryption", + "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", + "Kubernetes Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", + "Metadata", "Premium Plans", "Placement Group", "StackScripts", "Maintenance + Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], + "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.236.0.46,172.236.0.50,172.236.0.47,172.236.0.53,172.236.0.52,172.236.0.45,172.236.0.49,172.236.0.51,172.236.0.54,172.236.0.48", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "fr-par", - "label": "Paris, FR", "country": "fr", "capabilities": ["Linodes", "Block Storage - Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud - Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", - "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "au-mel", + "label": "Melbourne, AU", "country": "au", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", + "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.232.32.21,172.232.32.23,172.232.32.17,172.232.32.18,172.232.32.16,172.232.32.22,172.232.32.20,172.232.32.14,172.232.32.11,172.232.32.12", + {"ipv4": "172.236.32.23,172.236.32.35,172.236.32.30,172.236.32.28,172.236.32.32,172.236.32.33,172.236.32.27,172.236.32.37,172.236.32.29,172.236.32.34", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-sea", - "label": "Seattle, WA", "country": "us", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud - Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", - "Placement Group", "StackScripts", "Maintenance Policy", "Linode Interfaces"], - "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, - "status": "ok", "resolvers": {"ipv4": "172.232.160.19,172.232.160.21,172.232.160.17,172.232.160.15,172.232.160.18,172.232.160.8,172.232.160.12,172.232.160.11,172.232.160.14,172.232.160.16", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-bom-2", + "label": "Mumbai 2, IN", "country": "in", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "172.236.171.41,172.236.171.42,172.236.171.25,172.236.171.44,172.236.171.26,172.236.171.45,172.236.171.24,172.236.171.43,172.236.171.27,172.236.171.28", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "br-gru", - "label": "Sao Paulo, BR", "country": "br", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.233.0.4,172.233.0.9,172.233.0.7,172.233.0.12,172.233.0.5,172.233.0.13,172.233.0.10,172.233.0.6,172.233.0.8,172.233.0.11", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "de-fra-2", + "label": "Frankfurt 2, DE", "country": "de", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": + [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "172.236.203.9,172.236.203.16,172.236.203.19,172.236.203.15,172.236.203.17,172.236.203.11,172.236.203.18,172.236.203.14,172.236.203.13,172.236.203.12", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "nl-ams", - "label": "Amsterdam, NL", "country": "nl", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.233.33.36,172.233.33.38,172.233.33.35,172.233.33.39,172.233.33.34,172.233.33.33,172.233.33.31,172.233.33.30,172.233.33.37,172.233.33.32", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "sg-sin-2", + "label": "Singapore 2, SG", "country": "sg", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.236.129.8,172.236.129.42,172.236.129.41,172.236.129.19,172.236.129.46,172.236.129.23,172.236.129.48,172.236.129.20,172.236.129.21,172.236.129.47", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "se-sto", - "label": "Stockholm, SE", "country": "se", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.232.128.24,172.232.128.26,172.232.128.20,172.232.128.22,172.232.128.25,172.232.128.19,172.232.128.23,172.232.128.18,172.232.128.21,172.232.128.27", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "jp-tyo-3", + "label": "Tokyo 3, JP", "country": "jp", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "172.237.4.15,172.237.4.19,172.237.4.17,172.237.4.21,172.237.4.16,172.237.4.18,172.237.4.23,172.237.4.24,172.237.4.20,172.237.4.14", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "es-mad", - "label": "Madrid, ES", "country": "es", "capabilities": ["Linodes", "Block Storage - Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "de-ber-1", + "label": "Berlin, DE", "country": "de", "capabilities": ["Linodes", "Disk Encryption", + "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed Plans"], "monitors": + {"alerts": [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", + "ipv6": "1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + 0, "maximum_linodes_per_pg": 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": + "distributed"}, {"id": "no-osl-1", "label": "Oslo, NO", "country": "no", "capabilities": + ["Linodes", "Block Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.233.111.6,172.233.111.17,172.233.111.21,172.233.111.25,172.233.111.19,172.233.111.12,172.233.111.26,172.233.111.16,172.233.111.18,172.233.111.9", - "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, - "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-maa", - "label": "Chennai, IN", "country": "in", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": - ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.232.96.17,172.232.96.26,172.232.96.19,172.232.96.20,172.232.96.25,172.232.96.21,172.232.96.18,172.232.96.22,172.232.96.23,172.232.96.24", + "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.238.140.33,172.238.140.25,172.238.140.30,172.238.140.31,172.238.140.26,172.238.140.28,172.238.140.34,172.238.140.32,172.238.140.27,172.238.140.29", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "jp-osa", "label": "Osaka, JP", "country": "jp", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.233.64.44,172.233.64.43,172.233.64.37,172.233.64.40,172.233.64.46,172.233.64.41,172.233.64.39,172.233.64.42,172.233.64.45,172.233.64.38", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-west", + "label": "Mumbai, IN", "country": "in", "capabilities": ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud - Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", - "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": - ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.233.64.44,172.233.64.43,172.233.64.37,172.233.64.40,172.233.64.46,172.233.64.41,172.233.64.39,172.233.64.42,172.233.64.45,172.233.64.38", + "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", + "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Linode + Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed + Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.105.34.5,172.105.35.5,172.105.36.5,172.105.37.5,172.105.38.5,172.105.39.5,172.105.40.5,172.105.41.5,172.105.42.5,172.105.43.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "it-mil", - "label": "Milan, IT", "country": "it", "capabilities": ["Linodes", "Block Storage + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ca-central", + "label": "Toronto, CA", "country": "ca", "capabilities": ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.232.192.19,172.232.192.18,172.232.192.16,172.232.192.20,172.232.192.24,172.232.192.21,172.232.192.22,172.232.192.17,172.232.192.15,172.232.192.23", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.105.0.5,172.105.3.5,172.105.4.5,172.105.5.5,172.105.6.5,172.105.7.5,172.105.8.5,172.105.9.5,172.105.10.5,172.105.11.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-mia", - "label": "Miami, FL", "country": "us", "capabilities": ["Linodes", "Block Storage + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-southeast", + "label": "Sydney, AU", "country": "au", "capabilities": ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": - ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.233.160.34,172.233.160.27,172.233.160.30,172.233.160.29,172.233.160.32,172.233.160.28,172.233.160.33,172.233.160.26,172.233.160.25,172.233.160.31", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.105.166.5,172.105.169.5,172.105.168.5,172.105.172.5,172.105.162.5,172.105.170.5,172.105.167.5,172.105.171.5,172.105.181.5,172.105.161.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "id-cgk", - "label": "Jakarta, ID", "country": "id", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.232.224.23,172.232.224.32,172.232.224.26,172.232.224.27,172.232.224.21,172.232.224.24,172.232.224.22,172.232.224.20,172.232.224.31,172.232.224.28", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-iad", + "label": "Washington, DC", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "139.144.192.62,139.144.192.60,139.144.192.61,139.144.192.53,139.144.192.54,139.144.192.67,139.144.192.69,139.144.192.66,139.144.192.52,139.144.192.68", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-lax", - "label": "Los Angeles, CA", "country": "us", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "NETINT Quadra T1U", "Maintenance Policy", "Linode Interfaces"], - "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, - "status": "ok", "resolvers": {"ipv4": "172.233.128.45,172.233.128.38,172.233.128.53,172.233.128.37,172.233.128.34,172.233.128.36,172.233.128.33,172.233.128.39,172.233.128.43,172.233.128.44", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-ord", + "label": "Chicago, IL", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.0.17,172.232.0.16,172.232.0.21,172.232.0.13,172.232.0.22,172.232.0.9,172.232.0.19,172.232.0.20,172.232.0.15,172.232.0.18", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "gb-lon", - "label": "London 2, UK", "country": "gb", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", - "StackScripts", "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": - ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.236.0.46,172.236.0.50,172.236.0.47,172.236.0.53,172.236.0.52,172.236.0.45,172.236.0.49,172.236.0.51,172.236.0.54,172.236.0.48", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "fr-par", + "label": "Paris, FR", "country": "fr", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.32.21,172.232.32.23,172.232.32.17,172.232.32.18,172.232.32.16,172.232.32.22,172.232.32.20,172.232.32.14,172.232.32.11,172.232.32.12", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "au-mel", - "label": "Melbourne, AU", "country": "au", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", "VPCs", "Managed - Databases", "Metadata", "Premium Plans", "Placement Group", "StackScripts", - "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": ["Managed - Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.236.32.23,172.236.32.35,172.236.32.30,172.236.32.28,172.236.32.32,172.236.32.33,172.236.32.27,172.236.32.37,172.236.32.29,172.236.32.34", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-sea", + "label": "Seattle, WA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Maintenance Policy", "Linode + Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed + Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.232.160.19,172.232.160.21,172.232.160.17,172.232.160.15,172.232.160.18,172.232.160.8,172.232.160.12,172.232.160.11,172.232.160.14,172.232.160.16", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-bom-2", - "label": "Mumbai 2, IN", "country": "in", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans", "Placement Group", "StackScripts", "Linode - Interfaces"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": - {"ipv4": "172.236.171.41,172.236.171.42,172.236.171.25,172.236.171.44,172.236.171.26,172.236.171.45,172.236.171.24,172.236.171.43,172.236.171.27,172.236.171.28", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "br-gru", + "label": "Sao Paulo, BR", "country": "br", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.0.4,172.233.0.9,172.233.0.7,172.233.0.12,172.233.0.5,172.233.0.13,172.233.0.10,172.233.0.6,172.233.0.8,172.233.0.11", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "de-fra-2", - "label": "Frankfurt 2, DE", "country": "de", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans", "Placement Group", "StackScripts", "NETINT - Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": []}, - "status": "ok", "resolvers": {"ipv4": "172.236.203.9,172.236.203.16,172.236.203.19,172.236.203.15,172.236.203.17,172.236.203.11,172.236.203.18,172.236.203.14,172.236.203.13,172.236.203.12", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "nl-ams", + "label": "Amsterdam, NL", "country": "nl", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.33.36,172.233.33.38,172.233.33.35,172.233.33.39,172.233.33.34,172.233.33.33,172.233.33.31,172.233.33.30,172.233.33.37,172.233.33.32", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "sg-sin-2", - "label": "Singapore 2, SG", "country": "sg", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "se-sto", + "label": "Stockholm, SE", "country": "se", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "172.236.129.8,172.236.129.42,172.236.129.41,172.236.129.19,172.236.129.46,172.236.129.23,172.236.129.48,172.236.129.20,172.236.129.21,172.236.129.47", + {"ipv4": "172.232.128.24,172.232.128.26,172.232.128.20,172.232.128.22,172.232.128.25,172.232.128.19,172.232.128.23,172.232.128.18,172.232.128.21,172.232.128.27", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "jp-tyo-3", - "label": "Tokyo 3, JP", "country": "jp", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans", "Placement Group", "StackScripts", "Linode - Interfaces"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": - {"ipv4": "172.237.4.15,172.237.4.19,172.237.4.17,172.237.4.21,172.237.4.16,172.237.4.18,172.237.4.23,172.237.4.24,172.237.4.20,172.237.4.14", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "es-mad", + "label": "Madrid, ES", "country": "es", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.111.6,172.233.111.17,172.233.111.21,172.233.111.25,172.233.111.19,172.233.111.12,172.233.111.26,172.233.111.16,172.233.111.18,172.233.111.9", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "no-osl-1", - "label": "Oslo, NO", "country": "no", "capabilities": ["Linodes", "Block Storage - Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", - "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed - Databases", "Metadata", "Premium Plans", "Placement Group", "StackScripts", - "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": - ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.238.140.33,172.238.140.25,172.238.140.30,172.238.140.31,172.238.140.26,172.238.140.28,172.238.140.34,172.238.140.32,172.238.140.27,172.238.140.29", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-maa", + "label": "Chennai, IN", "country": "in", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.232.96.17,172.232.96.26,172.232.96.19,172.232.96.20,172.232.96.25,172.232.96.21,172.232.96.18,172.232.96.22,172.232.96.23,172.232.96.24", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-central", - "label": "Dallas, TX", "country": "us", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement - Group", "StackScripts", "Maintenance Policy", "Linode Interfaces"], "monitors": - {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": - "ok", "resolvers": {"ipv4": "72.14.179.5,72.14.188.5,173.255.199.5,66.228.53.5,96.126.122.5,96.126.124.5,96.126.127.5,198.58.107.5,198.58.111.5,23.239.24.5", + "label": "Dallas, TX", "country": "us", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Maintenance Policy", + "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "72.14.179.5,72.14.188.5,173.255.199.5,66.228.53.5,96.126.122.5,96.126.124.5,96.126.127.5,198.58.107.5,198.58.111.5,23.239.24.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-west", - "label": "Fremont, CA", "country": "us", "capabilities": ["Linodes", "Block - Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-northeast", + "label": "Tokyo 2, JP", "country": "jp", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": - ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "173.230.145.5, - 173.230.147.5, 173.230.155.5, 173.255.212.5, 173.255.219.5, 173.255.241.5, 173.255.243.5, - 173.255.244.5, 74.207.241.5, 74.207.242.5", "ipv6": "1234::5678, 1234::5678, - 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, - 1234::5678, 1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": - null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": - "core"}, {"id": "us-southeast", "label": "Atlanta, GA", "country": "us", "capabilities": - ["Linodes", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "139.162.66.5,139.162.67.5,139.162.68.5,139.162.69.5,139.162.70.5,139.162.71.5,139.162.72.5,139.162.73.5,139.162.74.5,139.162.75.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "eu-central", + "label": "Frankfurt, DE", "country": "de", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": - {"ipv4": "74.207.231.5,173.230.128.5,173.230.129.5,173.230.136.5,173.230.140.5,66.228.59.5,66.228.62.5,50.116.35.5,50.116.41.5,23.239.18.5", + {"ipv4": "139.162.130.5,139.162.131.5,139.162.132.5,139.162.133.5,139.162.134.5,139.162.135.5,139.162.136.5,139.162.137.5,139.162.138.5,139.162.139.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-east", - "label": "Newark, NJ", "country": "us", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Object Storage", "GPU Linodes", - "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed - Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], - "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, - "status": "ok", "resolvers": {"ipv4": "66.228.42.5,96.126.106.5,50.116.53.5,50.116.58.5,50.116.61.5,50.116.62.5,66.175.211.5,97.107.133.4,173.255.225.5,66.228.35.5", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-south", + "label": "Singapore, SG", "country": "sg", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block + Storage Migrations", "Metadata", "Placement Group", "StackScripts", "Maintenance + Policy", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": []}, "status": + "ok", "resolvers": {"ipv4": "139.162.11.5,139.162.13.5,139.162.14.5,139.162.15.5,139.162.16.5,139.162.21.5,139.162.27.5,103.3.60.18,103.3.60.19,103.3.60.20", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "eu-west", - "label": "London, UK", "country": "gb", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Metadata", "Placement Group", "StackScripts", - "Linode Interfaces"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", - "resolvers": {"ipv4": "178.79.182.5, 176.58.107.5, 176.58.116.5, 176.58.121.5, - 151.236.220.5, 212.71.252.5, 212.71.253.5, 109.74.192.20, 109.74.193.20, 109.74.194.20", - "ipv6": "1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, - 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678"}, "placement_group_limits": - {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": - 5}, "site_type": "core"}, {"id": "ap-south", "label": "Singapore, SG", "country": - "sg", "capabilities": ["Linodes", "Disk Encryption", "Backups", "NodeBalancers", + "label": "London, UK", "country": "gb", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Metadata", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "178.79.182.5, 176.58.107.5, + 176.58.116.5, 176.58.121.5, 151.236.220.5, 212.71.252.5, 212.71.253.5, 109.74.192.20, + 109.74.193.20, 109.74.194.20", "ipv6": "1234::5678, 1234::5678, 1234::5678, + 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, + 1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, + "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": + "core"}, {"id": "us-east", "label": "Newark, NJ", "country": "us", "capabilities": + ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Metadata", "Placement Group", "StackScripts", - "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": - []}, "status": "ok", "resolvers": {"ipv4": "139.162.11.5,139.162.13.5,139.162.14.5,139.162.15.5,139.162.16.5,139.162.21.5,139.162.27.5,103.3.60.18,103.3.60.19,103.3.60.20", - "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, - "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "eu-central", - "label": "Frankfurt, DE", "country": "de", "capabilities": ["Linodes", "Disk - Encryption", "Backups", "NodeBalancers", "Block Storage", "Object Storage", - "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", - "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Maintenance - Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], - "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "139.162.130.5,139.162.131.5,139.162.132.5,139.162.133.5,139.162.134.5,139.162.135.5,139.162.136.5,139.162.137.5,139.162.138.5,139.162.139.5", + "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement + Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed + Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "66.228.42.5,96.126.106.5,50.116.53.5,50.116.58.5,50.116.61.5,50.116.62.5,66.175.211.5,97.107.133.4,173.255.225.5,66.228.35.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-northeast", - "label": "Tokyo 2, JP", "country": "jp", "capabilities": ["Linodes", "Disk Encryption", - "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", - "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement - Group", "StackScripts", "Maintenance Policy", "Linode Interfaces"], "monitors": - {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": - "ok", "resolvers": {"ipv4": "139.162.66.5,139.162.67.5,139.162.68.5,139.162.69.5,139.162.70.5,139.162.71.5,139.162.72.5,139.162.73.5,139.162.74.5,139.162.75.5", + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-southeast", + "label": "Atlanta, GA", "country": "us", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block + Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", + "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed + Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "74.207.231.5,173.230.128.5,173.230.129.5,173.230.136.5,173.230.140.5,66.228.59.5,66.228.62.5,50.116.35.5,50.116.41.5,23.239.18.5", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": - 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}], "page": 1, - "pages": 1, "results": 32}' + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-west", + "label": "Fremont, CA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", + "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Maintenance + Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], + "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "173.230.145.5, + 173.230.147.5, 173.230.155.5, 173.255.212.5, 173.255.219.5, 173.255.241.5, 173.255.243.5, + 173.255.244.5, 74.207.241.5, 74.207.242.5", "ipv6": "1234::5678, 1234::5678, + 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, + 1234::5678, 1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + null, "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": + "core"}], "page": 1, "pages": 1, "results": 43}' headers: Access-Control-Allow-Credentials: - "true" @@ -338,7 +412,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:46 GMT + - Wed, 03 Sep 2025 20:15:08 GMT Pragma: - no-cache Strict-Transport-Security: @@ -364,7 +438,7 @@ interactions: code: 200 duration: "" - request: - body: '{"region":"us-sea","type":"g6-nanode-1","label":"go-test-ins-wo-disk-lqn29g74tv02","firewall_id":3118155,"booted":false}' + body: '{"region":"us-lax","type":"g6-nanode-1","label":"go-test-ins-wo-disk-43n0hqub362s","firewall_id":3167647,"booted":false}' form: {} headers: Accept: @@ -376,18 +450,19 @@ interactions: url: https://api.linode.com/v4beta/linode/instances method: POST response: - body: '{"id": 82372950, "label": "go-test-ins-wo-disk-lqn29g74tv02", "group": + body: '{"id": 83004980, "label": "go-test-ins-wo-disk-43n0hqub362s", "group": "", "status": "provisioning", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", - "type": "g6-nanode-1", "ipv4": ["172.238.40.152"], "ipv6": "1234::5678/128", - "image": null, "region": "us-sea", "site_type": "core", "specs": {"disk": 25600, + "type": "g6-nanode-1", "ipv4": ["172.236.253.154"], "ipv6": "1234::5678/128", + "image": null, "region": "us-lax", "site_type": "core", "specs": {"disk": 25600, "memory": 1024, "vcpus": 1, "gpus": 0, "transfer": 1000, "accelerated_devices": 0}, "alerts": {"cpu": 90, "network_in": 10, "network_out": 10, "transfer_quota": - 80, "io": 10000}, "backups": {"enabled": false, "available": false, "schedule": + 80, "io": 10000}, "backups": {"enabled": true, "available": false, "schedule": {"day": null, "window": null}, "last_successful": null}, "hypervisor": "kvm", - "watchdog_enabled": true, "tags": [], "host_uuid": "2821119d92bf7c65e7c36fe4263dd48f7b277a30", + "watchdog_enabled": true, "tags": [], "host_uuid": "5044a7bb40c6fb9a183e53d5d6b939cefcb78ee2", "has_user_data": false, "placement_group": null, "disk_encryption": "enabled", - "lke_cluster_id": null, "capabilities": ["Block Storage Encryption", "Maintenance - Policy"], "interface_generation": "legacy_config", "maintenance_policy": "linode/power_off_on"}' + "lke_cluster_id": null, "capabilities": ["Block Storage Encryption", "SMTP Enabled", + "Maintenance Policy"], "interface_generation": "legacy_config", "maintenance_policy": + "linode/migrate"}' headers: Access-Control-Allow-Credentials: - "true" @@ -410,7 +485,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:47 GMT + - Wed, 03 Sep 2025 20:15:09 GMT Pragma: - no-cache Strict-Transport-Security: @@ -435,7 +510,7 @@ interactions: code: 200 duration: "" - request: - body: '{"label":"go-test-conf-c9519pk6tp6j","devices":{},"interfaces":null}' + body: '{"label":"go-test-conf-080bi2w0lx4d","devices":{},"interfaces":null}' form: {} headers: Accept: @@ -444,10 +519,10 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/linode/instances/82372950/configs + url: https://api.linode.com/v4beta/linode/instances/83004980/configs method: POST response: - body: '{"id": 85836865, "label": "go-test-conf-c9519pk6tp6j", "helpers": {"updatedb_disabled": + body: '{"id": 86477537, "label": "go-test-conf-080bi2w0lx4d", "helpers": {"updatedb_disabled": true, "distro": true, "modules_dep": true, "network": false, "devtmpfs_automount": true}, "kernel": "linode/latest-64bit", "comments": "", "memory_limit": 0, "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "root_device": "/dev/sda", @@ -478,7 +553,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:48 GMT + - Wed, 03 Sep 2025 20:15:09 GMT Pragma: - no-cache Strict-Transport-Security: @@ -511,7 +586,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/linode/instances/82372950/transfer/2025/8 + url: https://api.linode.com/v4beta/linode/instances/83004980/transfer/2025/9 method: GET response: body: '{"bytes_in": 0, "bytes_out": 0, "bytes_total": 0}' @@ -539,7 +614,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:48 GMT + - Wed, 03 Sep 2025 20:15:10 GMT Pragma: - no-cache Strict-Transport-Security: @@ -573,7 +648,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/linode/instances/82372950/transfer/2025/8 + url: https://api.linode.com/v4beta/linode/instances/83004980/transfer/2025/9 method: GET response: body: '{"bytes_in": 0, "bytes_out": 0, "bytes_total": 0}' @@ -601,7 +676,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:48 GMT + - Wed, 03 Sep 2025 20:15:10 GMT Pragma: - no-cache Strict-Transport-Security: @@ -635,7 +710,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/linode/instances/82372950 + url: https://api.linode.com/v4beta/linode/instances/83004980 method: DELETE response: body: '{}' @@ -663,7 +738,7 @@ interactions: Content-Type: - application/json Expires: - - Fri, 22 Aug 2025 21:05:50 GMT + - Wed, 03 Sep 2025 20:15:11 GMT Pragma: - no-cache Strict-Transport-Security: diff --git a/test/integration/fixtures/TestLKECluster_Enterprise_BYOVPC_smoke.yaml b/test/integration/fixtures/TestLKECluster_Enterprise_BYOVPC_smoke.yaml new file mode 100644 index 000000000..4c1a2d547 --- /dev/null +++ b/test/integration/fixtures/TestLKECluster_Enterprise_BYOVPC_smoke.yaml @@ -0,0 +1,256 @@ +--- +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/lke/tiers/enterprise/versions?page=1 + method: GET + response: + body: '{"data": [{"id": "v1.31.8+lke5", "tier": "enterprise"}], "page": 1, "pages": + 1, "results": 1}' + 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-Length: + - "93" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:21 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - lke:read_only + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"node_pools":[{"count":1,"type":"g6-standard-2","disks":null,"tags":["test"],"labels":null,"taints":null}],"label":"go-test-def","region":"no-osl-1","k8s_version":"v1.31.8+lke5","tags":["testing"],"tier":"enterprise","vpc_id":224219,"stack_type":"ipv4-ipv6"}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/lke/clusters + method: POST + response: + body: '{"id": 508689, "status": "ready", "created": "2018-01-02T03:04:05", "updated": + "2018-01-02T03:04:05", "label": "go-test-def", "region": "no-osl-1", "k8s_version": + "v1.31.8+lke5", "tier": "enterprise", "control_plane": {"high_availability": + true, "audit_logs_enabled": false}, "apl_enabled": false, "vpc_id": 224219, + "subnet_id": null, "tags": ["testing"], "stack_type": "ipv4-ipv6"}' + 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-Length: + - "382" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:27 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - lke:read_write + 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: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/lke/clusters/508689 + method: GET + response: + body: '{"id": 508689, "status": "ready", "created": "2018-01-02T03:04:05", "updated": + "2018-01-02T03:04:05", "label": "go-test-def", "region": "no-osl-1", "k8s_version": + "v1.31.8+lke5", "tier": "enterprise", "control_plane": {"high_availability": + true, "audit_logs_enabled": false}, "apl_enabled": false, "vpc_id": 224219, + "subnet_id": null, "tags": ["testing"], "stack_type": "ipv4-ipv6"}' + 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-Length: + - "382" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:27 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - lke: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: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/lke/clusters/508689 + method: DELETE + response: + body: '{}' + 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-Length: + - "2" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:29 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - lke:read_write + 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/TestLKECluster_Enterprise_VPC_smoke.yaml b/test/integration/fixtures/TestLKECluster_Enterprise_VPC_smoke.yaml new file mode 100644 index 000000000..d16342d0d --- /dev/null +++ b/test/integration/fixtures/TestLKECluster_Enterprise_VPC_smoke.yaml @@ -0,0 +1,563 @@ +--- +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/regions?page=1 + method: GET + response: + body: '{"data": [{"id": "ap-west", "label": "Mumbai, IN", "country": "in", "capabilities": + ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block + Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", + "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.105.34.5,172.105.35.5,172.105.36.5,172.105.37.5,172.105.38.5,172.105.39.5,172.105.40.5,172.105.41.5,172.105.42.5,172.105.43.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ca-central", + "label": "Toronto, CA", "country": "ca", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.105.0.5,172.105.3.5,172.105.4.5,172.105.5.5,172.105.6.5,172.105.7.5,172.105.8.5,172.105.9.5,172.105.10.5,172.105.11.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-southeast", + "label": "Sydney, AU", "country": "au", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.105.166.5,172.105.169.5,172.105.168.5,172.105.172.5,172.105.162.5,172.105.170.5,172.105.167.5,172.105.171.5,172.105.181.5,172.105.161.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-iad", + "label": "Washington, DC", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "139.144.192.62,139.144.192.60,139.144.192.61,139.144.192.53,139.144.192.54,139.144.192.67,139.144.192.69,139.144.192.66,139.144.192.52,139.144.192.68", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-ord", + "label": "Chicago, IL", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.0.17,172.232.0.16,172.232.0.21,172.232.0.13,172.232.0.22,172.232.0.9,172.232.0.19,172.232.0.20,172.232.0.15,172.232.0.18", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "fr-par", + "label": "Paris, FR", "country": "fr", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.32.21,172.232.32.23,172.232.32.17,172.232.32.18,172.232.32.16,172.232.32.22,172.232.32.20,172.232.32.14,172.232.32.11,172.232.32.12", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-sea", + "label": "Seattle, WA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.232.160.19,172.232.160.21,172.232.160.17,172.232.160.15,172.232.160.18,172.232.160.8,172.232.160.12,172.232.160.11,172.232.160.14,172.232.160.16", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "br-gru", + "label": "Sao Paulo, BR", "country": "br", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.0.4,172.233.0.9,172.233.0.7,172.233.0.12,172.233.0.5,172.233.0.13,172.233.0.10,172.233.0.6,172.233.0.8,172.233.0.11", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "nl-ams", + "label": "Amsterdam, NL", "country": "nl", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.33.36,172.233.33.38,172.233.33.35,172.233.33.39,172.233.33.34,172.233.33.33,172.233.33.31,172.233.33.30,172.233.33.37,172.233.33.32", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "se-sto", + "label": "Stockholm, SE", "country": "se", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.232.128.24,172.232.128.26,172.232.128.20,172.232.128.22,172.232.128.25,172.232.128.19,172.232.128.23,172.232.128.18,172.232.128.21,172.232.128.27", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "es-mad", + "label": "Madrid, ES", "country": "es", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.233.111.6,172.233.111.17,172.233.111.21,172.233.111.25,172.233.111.19,172.233.111.12,172.233.111.26,172.233.111.16,172.233.111.18,172.233.111.9", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-maa", + "label": "Chennai, IN", "country": "in", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.232.96.17,172.232.96.26,172.232.96.19,172.232.96.20,172.232.96.25,172.232.96.21,172.232.96.18,172.232.96.22,172.232.96.23,172.232.96.24", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "jp-osa", + "label": "Osaka, JP", "country": "jp", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.233.64.44,172.233.64.43,172.233.64.37,172.233.64.40,172.233.64.46,172.233.64.41,172.233.64.39,172.233.64.42,172.233.64.45,172.233.64.38", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "it-mil", + "label": "Milan, IT", "country": "it", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.232.192.19,172.232.192.18,172.232.192.16,172.232.192.20,172.232.192.24,172.232.192.21,172.232.192.22,172.232.192.17,172.232.192.15,172.232.192.23", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-mia", + "label": "Miami, FL", "country": "us", "capabilities": ["Linodes", "Block Storage + Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.233.160.34,172.233.160.27,172.233.160.30,172.233.160.29,172.233.160.32,172.233.160.28,172.233.160.33,172.233.160.26,172.233.160.25,172.233.160.31", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "id-cgk", + "label": "Jakarta, ID", "country": "id", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.232.224.23,172.232.224.32,172.232.224.26,172.232.224.27,172.232.224.21,172.232.224.24,172.232.224.22,172.232.224.20,172.232.224.31,172.232.224.28", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-lax", + "label": "Los Angeles, CA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans", + "Placement Group", "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "172.233.128.45,172.233.128.38,172.233.128.53,172.233.128.37,172.233.128.34,172.233.128.36,172.233.128.33,172.233.128.39,172.233.128.43,172.233.128.44", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "nz-akl-1", + "label": "Auckland, NZ", "country": "nz", "capabilities": ["Linodes", "Disk + Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed Plans"], + "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": + "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "us-den-1", "label": "Denver, CO", "country": "us", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "de-ham-1", "label": "Hamburg, DE", "country": "de", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "fr-mrs-1", "label": "Marseille, FR", "country": "fr", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "za-jnb-1", "label": "Johannesburg, ZA", "country": "za", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "my-kul-1", "label": "Kuala Lumpur, MY", "country": "my", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "co-bog-1", "label": "Bogot\u00e1, CO", "country": "co", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "mx-qro-1", "label": "Quer\u00e9taro, MX", "country": "mx", "capabilities": + ["Linodes", "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", + "Distributed Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", + "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "us-hou-1", "label": "Houston, TX", "country": "us", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "cl-scl-1", "label": "Santiago, CL", "country": "cl", "capabilities": ["Linodes", + "Disk Encryption", "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed + Plans"], "monitors": {"alerts": [], "metrics": []}, "status": "ok", "resolvers": + {"ipv4": "173.223.100.53,173.223.101.53", "ipv6": "1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 0, "maximum_linodes_per_pg": + 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": "distributed"}, {"id": + "gb-lon", "label": "London 2, UK", "country": "gb", "capabilities": ["Linodes", + "Block Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", + "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.236.0.46,172.236.0.50,172.236.0.47,172.236.0.53,172.236.0.52,172.236.0.45,172.236.0.49,172.236.0.51,172.236.0.54,172.236.0.48", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "au-mel", + "label": "Melbourne, AU", "country": "au", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", + "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": + ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "172.236.32.23,172.236.32.35,172.236.32.30,172.236.32.28,172.236.32.32,172.236.32.33,172.236.32.27,172.236.32.37,172.236.32.29,172.236.32.34", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "in-bom-2", + "label": "Mumbai 2, IN", "country": "in", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "172.236.171.41,172.236.171.42,172.236.171.25,172.236.171.44,172.236.171.26,172.236.171.45,172.236.171.24,172.236.171.43,172.236.171.27,172.236.171.28", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "de-fra-2", + "label": "Frankfurt 2, DE", "country": "de", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "GPU Linodes", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "NETINT Quadra T1U", "Linode Interfaces"], "monitors": {"alerts": + [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "172.236.203.9,172.236.203.16,172.236.203.19,172.236.203.15,172.236.203.17,172.236.203.11,172.236.203.18,172.236.203.14,172.236.203.13,172.236.203.12", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "sg-sin-2", + "label": "Singapore 2, SG", "country": "sg", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Kubernetes + Enterprise", "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", + "Premium Plans", "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": + {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, "status": + "ok", "resolvers": {"ipv4": "172.236.129.8,172.236.129.42,172.236.129.41,172.236.129.19,172.236.129.46,172.236.129.23,172.236.129.48,172.236.129.20,172.236.129.21,172.236.129.47", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "jp-tyo-3", + "label": "Tokyo 3, JP", "country": "jp", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud + Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "172.237.4.15,172.237.4.19,172.237.4.17,172.237.4.21,172.237.4.16,172.237.4.18,172.237.4.23,172.237.4.24,172.237.4.20,172.237.4.14", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "de-ber-1", + "label": "Berlin, DE", "country": "de", "capabilities": ["Linodes", "Disk Encryption", + "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Distributed Plans"], "monitors": + {"alerts": [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "173.223.100.53,173.223.101.53", + "ipv6": "1234::5678,1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": + 0, "maximum_linodes_per_pg": 0, "maximum_linodes_per_flexible_pg": 0}, "site_type": + "distributed"}, {"id": "no-osl-1", "label": "Oslo, NO", "country": "no", "capabilities": + ["Linodes", "Block Storage Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Kubernetes", "Kubernetes Enterprise", "Cloud Firewall", "Vlans", + "VPCs", "Managed Databases", "Metadata", "Premium Plans", "Placement Group", + "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], + "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "172.238.140.33,172.238.140.25,172.238.140.30,172.238.140.31,172.238.140.26,172.238.140.28,172.238.140.34,172.238.140.32,172.238.140.27,172.238.140.29", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-central", + "label": "Dallas, TX", "country": "us", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "72.14.179.5,72.14.188.5,173.255.199.5,66.228.53.5,96.126.122.5,96.126.124.5,96.126.127.5,198.58.107.5,198.58.111.5,23.239.24.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-west", + "label": "Fremont, CA", "country": "us", "capabilities": ["Linodes", "Block + Storage Encryption", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", + "Managed Databases", "Metadata", "Placement Group", "StackScripts", "Linode + Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed + Databases"]}, "status": "ok", "resolvers": {"ipv4": "173.230.145.5, 173.230.147.5, + 173.230.155.5, 173.255.212.5, 173.255.219.5, 173.255.241.5, 173.255.243.5, 173.255.244.5, + 74.207.241.5, 74.207.242.5", "ipv6": "1234::5678, 1234::5678, 1234::5678, + 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, + 1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, + "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": + "core"}, {"id": "us-southeast", "label": "Atlanta, GA", "country": "us", "capabilities": + ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", + "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata", "Placement + Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": ["Managed + Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "74.207.231.5,173.230.128.5,173.230.129.5,173.230.136.5,173.230.140.5,66.228.59.5,66.228.62.5,50.116.35.5,50.116.41.5,23.239.18.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "us-east", + "label": "Newark, NJ", "country": "us", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block + Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", + "Linode Interfaces"], "monitors": {"alerts": ["Managed Databases"], "metrics": + ["Managed Databases"]}, "status": "ok", "resolvers": {"ipv4": "66.228.42.5,96.126.106.5,50.116.53.5,50.116.58.5,50.116.61.5,50.116.62.5,66.175.211.5,97.107.133.4,173.255.225.5,66.228.35.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "eu-west", + "label": "London, UK", "country": "gb", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Metadata", + "Placement Group", "StackScripts", "Linode Interfaces"], "monitors": {"alerts": + [], "metrics": []}, "status": "ok", "resolvers": {"ipv4": "178.79.182.5, 176.58.107.5, + 176.58.116.5, 176.58.121.5, 151.236.220.5, 212.71.252.5, 212.71.253.5, 109.74.192.20, + 109.74.193.20, 109.74.194.20", "ipv6": "1234::5678, 1234::5678, 1234::5678, + 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, 1234::5678, + 1234::5678"}, "placement_group_limits": {"maximum_pgs_per_customer": null, + "maximum_linodes_per_pg": 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": + "core"}, {"id": "ap-south", "label": "Singapore, SG", "country": "sg", "capabilities": + ["Linodes", "LA Disk Encryption", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", + "Vlans", "Block Storage Migrations", "Metadata", "Placement Group", "StackScripts", + "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": [], "metrics": + []}, "status": "ok", "resolvers": {"ipv4": "139.162.11.5,139.162.13.5,139.162.14.5,139.162.15.5,139.162.16.5,139.162.21.5,139.162.27.5,103.3.60.18,103.3.60.19,103.3.60.20", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "eu-central", + "label": "Frankfurt, DE", "country": "de", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block + Storage Migrations", "Managed Databases", "Metadata", "Placement Group", "StackScripts", + "Maintenance Policy", "Linode Interfaces"], "monitors": {"alerts": ["Managed + Databases"], "metrics": ["Managed Databases"]}, "status": "ok", "resolvers": + {"ipv4": "139.162.130.5,139.162.131.5,139.162.132.5,139.162.133.5,139.162.134.5,139.162.135.5,139.162.136.5,139.162.137.5,139.162.138.5,139.162.139.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}, {"id": "ap-northeast", + "label": "Tokyo 2, JP", "country": "jp", "capabilities": ["Linodes", "LA Disk + Encryption", "Disk Encryption", "Backups", "NodeBalancers", "Block Storage", + "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed + Databases", "Metadata", "Placement Group", "StackScripts", "Linode Interfaces"], + "monitors": {"alerts": ["Managed Databases"], "metrics": ["Managed Databases"]}, + "status": "ok", "resolvers": {"ipv4": "139.162.66.5,139.162.67.5,139.162.68.5,139.162.69.5,139.162.70.5,139.162.71.5,139.162.72.5,139.162.73.5,139.162.74.5,139.162.75.5", + "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": null, "maximum_linodes_per_pg": + 5, "maximum_linodes_per_flexible_pg": 5}, "site_type": "core"}], "page": 1, + "pages": 1, "results": 43}' + 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: + - Tue, 19 Aug 2025 16:56:20 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + - Accept-Encoding + X-Accepted-Oauth-Scopes: + - '*' + 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-vpc-1755622580345121000","region":"no-osl-1"}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/vpcs + method: POST + response: + body: '{"id": 224219, "label": "go-test-vpc-1755622580345121000", "description": + "", "region": "no-osl-1", "ipv6": null, "subnets": [], "created": "2018-01-02T03:04:05", + "updated": "2018-01-02T03:04:05"}' + 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-Length: + - "196" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:21 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - vpc:read_write + 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: "" + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/vpcs/224219 + method: DELETE + response: + body: '{}' + 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-Length: + - "2" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Expires: + - Tue, 19 Aug 2025 16:56:30 GMT + Pragma: + - no-cache + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - vpc:read_write + 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_instance.yaml b/test/integration/fixtures/TestMonitorAlertDefinition_instance.yaml new file mode 100644 index 000000000..bc8824789 --- /dev/null +++ b/test/integration/fixtures/TestMonitorAlertDefinition_instance.yaml @@ -0,0 +1,173 @@ +--- +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-definitions?page=1 + method: GET + response: + body: | + { + "data": [ + { + "id": 10000, + "label": "High Memory Usage Plan Dedicated", + "severity": 2, + "type": "system", + "service_type": "dbaas", + "status": "enabled", + "has_more_resources": false, + "rule": null, + "trigger_conditions": { + "criteria_condition": "ALL", + "evaluation_period_seconds": 300, + "polling_interval_seconds": 300, + "trigger_occurrences": 1 + }, + "alert_channels": [ + { + "id": 10000, + "label": "Read-Write Channel", + "type": "alert-channels", + "url": "/monitor/alert-channels/10000" + } + ], + "created": "2025-03-20T01:42:11", + "updated": "2025-09-11T04:45:19", + "updated_by": "system", + "created_by": "system", + "entity_ids": [], + "description": "Alert triggers when dedicated plan nodes consistently reach critical memory usage, risking application performance degradation.", + "class": "dedicated" + }, + { + "id": 10001, + "label": "High Memory Usage Plan Shared", + "severity": 2, + "type": "system", + "service_type": "dbaas", + "status": "enabled", + "has_more_resources": false, + "rule": null, + "trigger_conditions": { + "criteria_condition": "ALL", + "evaluation_period_seconds": 300, + "polling_interval_seconds": 300, + "trigger_occurrences": 1 + }, + "alert_channels": [ + { + "id": 10000, + "label": "Read-Write Channel", + "type": "alert-channels", + "url": "/monitor/alert-channels/10000" + } + ], + "created": "2025-03-20T01:42:11", + "updated": "2025-09-11T04:25:12", + "updated_by": "system", + "created_by": "system", + "entity_ids": [189690, 313392, 324828, 340839], + "description": "Alert triggers when shared plan nodes consistently reach critical memory usage, risking application performance degradation.", + "class": "shared" + } + ], + "page": 1, + "pages": 1, + "results": 2 + } + headers: + Content-Type: + - application/json + status: 200 OK + code: 200 + duration: "" +- 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/services/dbaas/alert-definitions/10001 + method: GET + response: + body: | + { + "id": 10001, + "label": "High Memory Usage Plan Shared", + "severity": 2, + "type": "system", + "service_type": "dbaas", + "status": "enabled", + "has_more_resources": false, + "rule": null, + "trigger_conditions": { + "criteria_condition": "ALL", + "evaluation_period_seconds": 300, + "polling_interval_seconds": 300, + "trigger_occurrences": 1 + }, + "alert_channels": [ + { + "id": 10000, + "label": "Read-Write Channel", + "type": "alert-channels", + "url": "/monitor/alert-channels/10000" + } + ], + "created": "2025-03-20T01:42:11", + "updated": "2025-09-11T04:25:12", + "updated_by": "system", + "created_by": "system", + "entity_ids": [189690, 313392, 324828, 340839], + "description": "Alert triggers when shared plan nodes consistently reach critical memory usage, risking application performance degradation.", + "class": "shared" + } + headers: + Content-Type: + - application/json + status: 200 OK + code: 200 + duration: "" +- 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/ + method: GET + response: + body: | + { + "id": 10000, + "label": "Read-Write Channel", + "type": "alert-channels", + "details": { + "to": "ops@example.com" + }, + "created": "2025-03-20T01:42:11", + "updated": "2025-09-11T04:25:12" + } + headers: + Content-Type: + - application/json + status: 200 OK + code: 200 + duration: "" \ No newline at end of file diff --git a/test/integration/fixtures/TestUserGrants_Update.yaml b/test/integration/fixtures/TestUserGrants_Update.yaml index 8e9a8a2c3..145dd81ab 100644 --- a/test/integration/fixtures/TestUserGrants_Update.yaml +++ b/test/integration/fixtures/TestUserGrants_Update.yaml @@ -65,7 +65,7 @@ interactions: code: 200 duration: "" - request: - body: '{"global":{"account_access":"read_only","add_databases":true,"add_domains":false,"add_firewalls":true,"add_images":true,"add_linodes":false,"add_longview":true,"add_nodebalancers":false,"add_placement_groups":false,"add_stackscripts":true,"add_volumes":true,"add_vpcs":true,"cancel_account":false,"child_account_access":false,"longview_subscription":false}}' + body: '{"global":{"account_access":"read_only","add_databases":true,"add_domains":false,"add_firewalls":true,"add_images":true,"add_linodes":false,"add_longview":true,"add_nodebalancers":false,"add_stackscripts":true,"add_volumes":true,"add_vpcs":true,"cancel_account":false,"child_account_access":false,"longview_subscription":false}}' form: {} headers: Accept: diff --git a/test/integration/fixtures/TestUserGrants_UpdateNoAccess.yaml b/test/integration/fixtures/TestUserGrants_UpdateNoAccess.yaml index a537f4d7f..56e9a9f57 100644 --- a/test/integration/fixtures/TestUserGrants_UpdateNoAccess.yaml +++ b/test/integration/fixtures/TestUserGrants_UpdateNoAccess.yaml @@ -65,7 +65,7 @@ interactions: code: 200 duration: "" - request: - body: '{"global":{"account_access":null,"add_databases":false,"add_domains":false,"add_firewalls":false,"add_images":false,"add_linodes":false,"add_longview":false,"add_nodebalancers":false,"add_placement_groups":false,"add_stackscripts":false,"add_volumes":false,"add_vpcs":false,"cancel_account":false,"child_account_access":false,"longview_subscription":false}}' + body: '{"global":{"account_access":null,"add_databases":false,"add_domains":false,"add_firewalls":false,"add_images":false,"add_linodes":false,"add_longview":false,"add_nodebalancers":false,"add_stackscripts":false,"add_volumes":false,"add_vpcs":false,"cancel_account":false,"child_account_access":false,"longview_subscription":false}}' form: {} headers: Accept: diff --git a/test/integration/instance_interfaces_test.go b/test/integration/instance_interfaces_test.go index 933f52094..56e20180c 100644 --- a/test/integration/instance_interfaces_test.go +++ b/test/integration/instance_interfaces_test.go @@ -102,7 +102,7 @@ func TestInstance_CreateWithLinodeInterfaces( FirewallID: linodego.DoublePointer(firewallID), Public: &linodego.PublicInterfaceCreateOptions{ IPv4: &linodego.PublicInterfaceIPv4CreateOptions{ - Addresses: []linodego.PublicInterfaceIPv4AddressCreateOptions{ + Addresses: &[]linodego.PublicInterfaceIPv4AddressCreateOptions{ { Address: linodego.Pointer("auto"), Primary: linodego.Pointer(true), @@ -117,7 +117,7 @@ func TestInstance_CreateWithLinodeInterfaces( VPC: &linodego.VPCInterfaceCreateOptions{ SubnetID: vpcSubnet.ID, IPv4: &linodego.VPCInterfaceIPv4CreateOptions{ - Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{ + Addresses: &[]linodego.VPCInterfaceIPv4AddressCreateOptions{ { Address: linodego.Pointer("auto"), Primary: linodego.Pointer(true), diff --git a/test/integration/integration_suite_test.go b/test/integration/integration_suite_test.go index ae8a9fcd2..3ca0cb8a2 100644 --- a/test/integration/integration_suite_test.go +++ b/test/integration/integration_suite_test.go @@ -127,7 +127,7 @@ func testRecorder(t *testing.T, fixturesYaml string, testingMode recorder.Mode, recordStopper = func() { r.Stop() } - return + return r, recordStopper } // createTestClient is a testing helper to creates a linodego.Client initialized using diff --git a/test/integration/lke_clusters_test.go b/test/integration/lke_clusters_test.go index c326c5ce9..6d52bdc23 100644 --- a/test/integration/lke_clusters_test.go +++ b/test/integration/lke_clusters_test.go @@ -83,6 +83,48 @@ func TestLKECluster_Enterprise_smoke(t *testing.T) { } } +func TestLKECluster_Enterprise_BYOVPC_smoke(t *testing.T) { + // bring your own vpc + client, fixtureTeardown := createTestClient(t, "fixtures/TestLKECluster_Enterprise_VPC_smoke") + + region := "no-osl-1" + vpc, vpcTeardown, err := createVPC(t, client, []vpcModifier{func(l *linodego.Client, options *linodego.VPCCreateOptions) { + options.Region = region + }}...) + + client, lkeCluster, teardown, err := setupLKECluster(t, []clusterModifier{func(createOpts *linodego.LKEClusterCreateOptions) { + createOpts.Tier = "enterprise" + createOpts.Region = region + createOpts.K8sVersion = "" + createOpts.VpcID = linodego.Pointer(vpc.ID) + createOpts.StackType = linodego.Pointer(linodego.LKEClusterDualStack) + }}, + "fixtures/TestLKECluster_Enterprise_BYOVPC_smoke") + if err != nil { + t.Errorf("Error creating lke, GOT ERROR %v", err) + } + + defer func() { + teardown() + vpcTeardown() + fixtureTeardown() + }() + + cluster, err := client.GetLKECluster(context.Background(), lkeCluster.ID) + if err != nil { + t.Errorf("Error getting lkeCluster, expected struct, got %v and error %v", cluster, err) + } + if cluster.ID != lkeCluster.ID { + t.Errorf("Expected a specific lkeCluster, but got a different one %v", cluster) + } + if cluster.VpcID != vpc.ID { + t.Errorf("Expected an LKE cluster in VPC %v, but got in VPC %v.", vpc.ID, cluster.VpcID) + } + if cluster.StackType != linodego.LKEClusterDualStack { + t.Errorf("Expected an LKE cluster stack_type is %v, but got %v.", linodego.LKEClusterDualStack, cluster.StackType) + } +} + func TestLKECluster_Update(t *testing.T) { client, cluster, teardown, err := setupLKECluster(t, []clusterModifier{func(createOpts *linodego.LKEClusterCreateOptions) { createOpts.Label = "go-lke-test-update" @@ -389,6 +431,9 @@ func setupLKECluster(t *testing.T, clusterModifiers []clusterModifier, fixturesY Type: "g6-standard-2", Tags: []string{"test"}, }}, + VpcID: nil, // default, overridden if needed + SubnetID: nil, // default, overridden if needed + StackType: nil, // default, overridden if needed } for _, modifier := range clusterModifiers { diff --git a/test/integration/monitor_alert_definitions_test.go b/test/integration/monitor_alert_definitions_test.go new file mode 100644 index 000000000..6fef65d2b --- /dev/null +++ b/test/integration/monitor_alert_definitions_test.go @@ -0,0 +1,188 @@ +package integration + +import ( + "context" + "testing" + "time" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +const ( + testMonitorAlertDefinitionServiceType = "dbaas" +) + +func TestMonitorAlertDefinition_smoke(t *testing.T) { + client, teardown := createTestClient(t, "fixtures/TestMonitorAlertDefinition_instance") + defer teardown() + + //Get All Alert Definitions + alerts, err := client.ListMonitorAlertDefinitions(context.Background(), "", nil) + + //Even if there is no alert definition, it should not error out + if err != nil { + t.Fatalf("failed to fetch monitor alert definitions: %s", err) + } + + // New: Iterate and log each alert definition for visibility + for _, alert := range alerts { + // Check few mandatory fields on each listed alert + assert.NotZero(t, alert.ID, "alert.ID should not be zero") + assert.NotEmpty(t, alert.Label, "alert.Label should not be empty") + + // If alert has a rule, validate basic rule structure + if alert.RuleCriteria != nil { + assert.NotEmpty(t, alert.RuleCriteria.Rules, "RuleCriteria.Rules should not be empty when RuleCriteria is provided") + for _, r := range alert.RuleCriteria.Rules { + assert.NotEmpty(t, r.Metric, "rule.Metric should not be empty") + assert.NotEmpty(t, r.Operator, "rule.Operator should not be empty") + } + } + } + + // 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") + + // Test creating a new Monitor Alert Definition + createOpts := linodego.AlertDefinitionCreateOptions{ + Label: "go-test-alert-definition-create", + Severity: int(linodego.SeverityLow), + Description: "Test alert definition creation", + ChannelIDs: []int{channelID}, + EntityIDs: nil, + TriggerConditions: &linodego.TriggerConditions{ + CriteriaCondition: "ALL", + EvaluationPeriodSeconds: 300, + PollingIntervalSeconds: 300, + TriggerOccurrences: 1, + }, + RuleCriteria: &linodego.RuleCriteria{ + Rules: []linodego.Rule{ + { + AggregateFunction: "avg", + Label: "Memory Usage", + Metric: "memory_usage", + Operator: "gt", + Threshold: func(f float64) *float64 { return &f }(90.0), + Unit: func(s string) *string { return &s }("percent"), + DimensionFilters: []linodego.DimensionFilter{ + { + DimensionLabel: "node_type", + Label: "Node Type", + Operator: "eq", + Value: "primary", + }, + }, + }, + }, + }, + } + + createdAlert, err := client.CreateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createOpts) + if err != nil { + // The test fixtures may return a 400 if an existing alert is being updated. + // Treat this as a non-fatal condition for the smoke test: log and exit. + t.Logf("CreateMonitorAlertDefinition returned error, skipping create assertions: %s", err) + return + } + assert.NoError(t, err) + assert.NotNil(t, createdAlert) + assert.Equal(t, createOpts.Label, createdAlert.Label) + assert.Equal(t, createOpts.Severity, createdAlert.Severity) + assert.Equal(t, createOpts.Description, createdAlert.Description) + assert.ElementsMatch(t, createOpts.EntityIDs, createdAlert.EntityIDs) + // assert.Equal(t, fetchedChannel.Label, createdAlert.AlertChannels[0].Label) + + // More thorough assertions on the created alert's nested fields + if createdAlert.TriggerConditions != nil && createOpts.TriggerConditions != nil { + assert.Equal(t, createOpts.TriggerConditions.CriteriaCondition, createdAlert.TriggerConditions.CriteriaCondition) + assert.Equal(t, createOpts.TriggerConditions.EvaluationPeriodSeconds, createdAlert.TriggerConditions.EvaluationPeriodSeconds) + assert.Equal(t, createOpts.TriggerConditions.PollingIntervalSeconds, createdAlert.TriggerConditions.PollingIntervalSeconds) + assert.Equal(t, createOpts.TriggerConditions.TriggerOccurrences, createdAlert.TriggerConditions.TriggerOccurrences) + } + if createdAlert.RuleCriteria != nil && createOpts.RuleCriteria != nil { + assert.Equal(t, len(createOpts.RuleCriteria.Rules), len(createdAlert.RuleCriteria.Rules), "created alert should have same number of rules") + for i, r := range createOpts.RuleCriteria.Rules { + cr := createdAlert.RuleCriteria.Rules[i] + assert.Equal(t, r.Metric, cr.Metric) + assert.Equal(t, r.Operator, cr.Operator) + if r.Threshold != nil { + assert.NotNil(t, cr.Threshold) + assert.Equal(t, *r.Threshold, *cr.Threshold) + } + // Dimension filters + if len(r.DimensionFilters) > 0 { + assert.Equal(t, len(r.DimensionFilters), len(cr.DimensionFilters)) + for j, df := range r.DimensionFilters { + cdf := cr.DimensionFilters[j] + assert.Equal(t, df.DimensionLabel, cdf.DimensionLabel) + assert.Equal(t, df.Operator, cdf.Operator) + assert.Equal(t, df.Value, cdf.Value) + } + } + } + } + + // Update the created alert definition: change label only + newLabel := createdAlert.Label + "-updated" + updateOpts := linodego.AlertDefinitionUpdateOptions{ + Label: newLabel, + } + // wait for 1 minute before update for create to complete + time.Sleep(1 * time.Minute) + updatedAlert, err := client.UpdateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createdAlert.ID, updateOpts) + if err != nil { + // Some fixtures may not support update; treat as non-fatal + t.Logf("UpdateMonitorAlertDefinition returned error, skipping update assertions: %s", err) + } else { + assert.NotNil(t, updatedAlert) + assert.Equal(t, createdAlert.ID, updatedAlert.ID, "updated alert should keep same ID") + assert.Equal(t, newLabel, updatedAlert.Label, "updated alert should have the new label") + } + + // Clean up created alert definition + if createdAlert != nil { + // Retry deletion with exponential backoff for up to 2 minutes + maxWait := 2 * time.Minute + baseDelay := 2 * time.Second + var lastErr error + start := time.Now() + for attempt := 0; time.Since(start) < maxWait; attempt++ { + err = client.DeleteMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createdAlert.ID) + if err == nil { + break + } + lastErr = err + // Exponential backoff, capped at 30s + sleep := baseDelay * (1 << attempt) + if sleep > 30*time.Second { + sleep = 30 * time.Second + } + time.Sleep(sleep) + } + assert.NoError(t, err, "DeleteMonitorAlertDefinition failed after retries: %v", lastErr) + } +} diff --git a/test/integration/test_retry.go b/test/integration/test_retry.go index 0d3fd0fd4..072a62ecc 100644 --- a/test/integration/test_retry.go +++ b/test/integration/test_retry.go @@ -1,6 +1,7 @@ package integration import ( + "errors" "fmt" "runtime" "testing" @@ -55,7 +56,7 @@ func (t *TRetry) Cleanup(f func()) { } func (t *TRetry) Error(args ...any) { - t.ErrorChannel <- fmt.Errorf(fmt.Sprint(args...)) + t.ErrorChannel <- errors.New(fmt.Sprint(args...)) } func (t *TRetry) Errorf(format string, args ...any) { @@ -72,7 +73,7 @@ func (t *TRetry) Failed() bool { } func (t *TRetry) Fatal(args ...any) { - t.ErrorChannel <- fmt.Errorf(fmt.Sprint(fmt.Sprint(args...))) + t.ErrorChannel <- errors.New(fmt.Sprint(fmt.Sprint(args...))) t.Fail() } diff --git a/test/integration/vpc_test.go b/test/integration/vpc_test.go index e3ec2ad04..0e4509004 100644 --- a/test/integration/vpc_test.go +++ b/test/integration/vpc_test.go @@ -6,10 +6,9 @@ import ( "strings" "testing" - "github.com/stretchr/testify/require" - "github.com/linode/linodego" . "github.com/linode/linodego" + "github.com/stretchr/testify/require" ) type vpcModifier func(*linodego.Client, *linodego.VPCCreateOptions) diff --git a/test/unit/account_user_grants_test.go b/test/unit/account_user_grants_test.go index 12731cfc6..4aefaa557 100644 --- a/test/unit/account_user_grants_test.go +++ b/test/unit/account_user_grants_test.go @@ -64,7 +64,6 @@ func TestAccountUserGrants_Get(t *testing.T) { assert.Equal(t, true, grants.Global.AddLinodes) assert.Equal(t, true, grants.Global.AddLongview) assert.Equal(t, true, grants.Global.AddNodeBalancers) - assert.Equal(t, true, grants.Global.AddPlacementGroups) assert.Equal(t, true, grants.Global.AddStackScripts) assert.Equal(t, true, grants.Global.AddVolumes) assert.Equal(t, true, grants.Global.AddVPCs) @@ -135,7 +134,6 @@ func TestAccountGrants_Update(t *testing.T) { assert.Equal(t, true, grants.Global.AddLinodes) assert.Equal(t, true, grants.Global.AddLongview) assert.Equal(t, true, grants.Global.AddNodeBalancers) - assert.Equal(t, true, grants.Global.AddPlacementGroups) assert.Equal(t, true, grants.Global.AddStackScripts) assert.Equal(t, true, grants.Global.AddVolumes) assert.Equal(t, true, grants.Global.AddVPCs) diff --git a/test/unit/base.go b/test/unit/base.go index f49287aae..b393f925e 100644 --- a/test/unit/base.go +++ b/test/unit/base.go @@ -2,6 +2,7 @@ package unit import ( "net/http" + "strings" "testing" "github.com/jarcoal/httpmock" @@ -38,24 +39,49 @@ func (c *ClientBaseCase) TearDown(t *testing.T) { func (c *ClientBaseCase) MockGet(path string, response interface{}) { fullURL := c.BaseURL + path httpmock.RegisterResponder("GET", fullURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + + // Also register beta endpoint equivalents for monitor-related endpoints + if strings.HasPrefix(path, "monitor/") { + altBase := strings.Replace(c.BaseURL, "/v4/", "/v4beta/", 1) + altURL := altBase + path + httpmock.RegisterResponder("GET", altURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + } } // MockPost mocks a POST request for a given path with the provided response body func (c *ClientBaseCase) MockPost(path string, response interface{}) { fullURL := c.BaseURL + path httpmock.RegisterResponder("POST", fullURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + + if strings.HasPrefix(path, "monitor/") { + altBase := strings.Replace(c.BaseURL, "/v4/", "/v4beta/", 1) + altURL := altBase + path + httpmock.RegisterResponder("POST", altURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + } } // MockPut mocks a PUT request for a given path with the provided response body func (c *ClientBaseCase) MockPut(path string, response interface{}) { fullURL := c.BaseURL + path httpmock.RegisterResponder("PUT", fullURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + + if strings.HasPrefix(path, "monitor/") { + altBase := strings.Replace(c.BaseURL, "/v4/", "/v4beta/", 1) + altURL := altBase + path + httpmock.RegisterResponder("PUT", altURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + } } // MockDelete mocks a DELETE request for a given path with the provided response body func (c *ClientBaseCase) MockDelete(path string, response interface{}) { fullURL := c.BaseURL + path httpmock.RegisterResponder("DELETE", fullURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + + if strings.HasPrefix(path, "monitor/") { + altBase := strings.Replace(c.BaseURL, "/v4/", "/v4beta/", 1) + altURL := altBase + path + httpmock.RegisterResponder("DELETE", altURL, httpmock.NewJsonResponderOrPanic(http.StatusOK, response)) + } } // MonitorClientBaseCase provides a base for unit tests diff --git a/test/unit/fixtures/account_user_grants_get.json b/test/unit/fixtures/account_user_grants_get.json index dc3913d1c..28f8c54f8 100644 --- a/test/unit/fixtures/account_user_grants_get.json +++ b/test/unit/fixtures/account_user_grants_get.json @@ -29,7 +29,6 @@ "add_linodes": true, "add_longview": true, "add_nodebalancers": true, - "add_placement_groups": true, "add_stackscripts": true, "add_volumes": true, "add_vpcs": true, diff --git a/test/unit/fixtures/account_user_grants_update.json b/test/unit/fixtures/account_user_grants_update.json index 52c06a926..ec490db4b 100644 --- a/test/unit/fixtures/account_user_grants_update.json +++ b/test/unit/fixtures/account_user_grants_update.json @@ -29,7 +29,6 @@ "add_linodes": true, "add_longview": true, "add_nodebalancers": true, - "add_placement_groups": true, "add_stackscripts": true, "add_volumes": true, "add_vpcs": true, diff --git a/test/unit/fixtures/lke_cluster_create.json b/test/unit/fixtures/lke_cluster_create.json index c83f388d2..7feb8a7dd 100644 --- a/test/unit/fixtures/lke_cluster_create.json +++ b/test/unit/fixtures/lke_cluster_create.json @@ -2,6 +2,12 @@ "id": 125, "label": "new-cluster", "region": "us-west", - "status": "ready" + "status": "ready", + "subnet_id": 123, + "vpc_id": 456, + "stack_type": "ipv4", + "control_plane": { + "audit_logs_enabled": false + } } \ No newline at end of file diff --git a/test/unit/fixtures/lke_cluster_get.json b/test/unit/fixtures/lke_cluster_get.json index fd67f74d8..965ed4cea 100644 --- a/test/unit/fixtures/lke_cluster_get.json +++ b/test/unit/fixtures/lke_cluster_get.json @@ -2,6 +2,12 @@ "id": 123, "label": "test-cluster", "region": "us-east", - "status": "ready" + "status": "ready", + "subnet_id": 123, + "vpc_id": 456, + "stack_type": "ipv4", + "control_plane": { + "audit_logs_enabled": false + } } \ No newline at end of file diff --git a/test/unit/fixtures/lke_cluster_list.json b/test/unit/fixtures/lke_cluster_list.json index f8ab363b4..ccce5dc00 100644 --- a/test/unit/fixtures/lke_cluster_list.json +++ b/test/unit/fixtures/lke_cluster_list.json @@ -4,7 +4,13 @@ "id": 123, "label": "test-cluster", "region": "us-east", - "status": "ready" + "status": "ready", + "subnet_id": 123, + "vpc_id": 456, + "stack_type": "ipv4", + "control_plane": { + "audit_logs_enabled": false + } }, { "id": 124, diff --git a/test/unit/fixtures/lke_cluster_update.json b/test/unit/fixtures/lke_cluster_update.json index 84b4d3137..af312568e 100644 --- a/test/unit/fixtures/lke_cluster_update.json +++ b/test/unit/fixtures/lke_cluster_update.json @@ -2,6 +2,9 @@ "id": 123, "label": "updated-cluster", "region": "us-east", - "status": "ready" + "status": "ready", + "control_plane": { + "audit_logs_enabled": true + } } \ No newline at end of file diff --git a/test/unit/fixtures/profile_grants_list.json b/test/unit/fixtures/profile_grants_list.json index dc3913d1c..28f8c54f8 100644 --- a/test/unit/fixtures/profile_grants_list.json +++ b/test/unit/fixtures/profile_grants_list.json @@ -29,7 +29,6 @@ "add_linodes": true, "add_longview": true, "add_nodebalancers": true, - "add_placement_groups": true, "add_stackscripts": true, "add_volumes": true, "add_vpcs": true, diff --git a/test/unit/instance_config_interfaces_test.go b/test/unit/instance_config_interfaces_test.go index 96857ad1a..c78f4e9a8 100644 --- a/test/unit/instance_config_interfaces_test.go +++ b/test/unit/instance_config_interfaces_test.go @@ -31,7 +31,7 @@ func TestInstanceConfigInterface_List(t *testing.T) { assert.Equal(t, "1234::5678/64", interfaces[0].IPv6.SLAAC[0].Range) assert.Equal(t, "1234::5678", interfaces[0].IPv6.SLAAC[0].Address) assert.Equal(t, "1234::5678/64", interfaces[0].IPv6.Ranges[0].Range) - assert.True(t, interfaces[0].IPv6.IsPublic) + assert.True(t, *interfaces[0].IPv6.IsPublic) } func TestInstanceConfigInterface_Get(t *testing.T) { @@ -56,7 +56,7 @@ func TestInstanceConfigInterface_Get(t *testing.T) { assert.Equal(t, "1234::5678/64", iface.IPv6.SLAAC[0].Range) assert.Equal(t, "1234::5678", iface.IPv6.SLAAC[0].Address) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.True(t, iface.IPv6.IsPublic) + assert.True(t, *iface.IPv6.IsPublic) } func TestInstanceConfigInterface_Create(t *testing.T) { @@ -95,7 +95,7 @@ func TestInstanceConfigInterface_Create(t *testing.T) { assert.Equal(t, "1234::5678/64", iface.IPv6.SLAAC[0].Range) assert.Equal(t, "1234::5678", iface.IPv6.SLAAC[0].Address) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.True(t, iface.IPv6.IsPublic) + assert.True(t, *iface.IPv6.IsPublic) } func TestInstanceConfigInterface_Update(t *testing.T) { @@ -130,7 +130,7 @@ func TestInstanceConfigInterface_Update(t *testing.T) { assert.Equal(t, "1234::5678/64", iface.IPv6.SLAAC[0].Range) assert.Equal(t, "1234::5678", iface.IPv6.SLAAC[0].Address) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.True(t, iface.IPv6.IsPublic) + assert.True(t, *iface.IPv6.IsPublic) } func TestInstanceConfigInterface_Delete(t *testing.T) { diff --git a/test/unit/instance_config_test.go b/test/unit/instance_config_test.go index 3b0ee76ee..34473dac3 100644 --- a/test/unit/instance_config_test.go +++ b/test/unit/instance_config_test.go @@ -43,7 +43,7 @@ func TestInstanceConfig_List(t *testing.T) { assert.Len(t, iface.IPv6.Ranges, 1) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.Equal(t, true, iface.IPv6.IsPublic) + assert.Equal(t, true, *iface.IPv6.IsPublic) assert.ElementsMatch(t, []string{"192.168.1.0/24"}, iface.IPRanges) } @@ -85,7 +85,7 @@ func TestInstanceConfig_Get(t *testing.T) { assert.Len(t, iface.IPv6.Ranges, 1) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.Equal(t, true, iface.IPv6.IsPublic) + assert.Equal(t, true, *iface.IPv6.IsPublic) assert.ElementsMatch(t, []string{"192.168.1.0/24"}, iface.IPRanges) } @@ -134,7 +134,7 @@ func TestInstanceConfig_Create(t *testing.T) { assert.Len(t, iface.IPv6.Ranges, 1) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.Equal(t, true, iface.IPv6.IsPublic) + assert.Equal(t, true, *iface.IPv6.IsPublic) assert.ElementsMatch(t, []string{"192.168.1.0/24"}, iface.IPRanges) } @@ -180,7 +180,7 @@ func TestInstanceConfig_Update(t *testing.T) { assert.Len(t, iface.IPv6.Ranges, 1) assert.Equal(t, "1234::5678/64", iface.IPv6.Ranges[0].Range) - assert.Equal(t, true, iface.IPv6.IsPublic) + assert.Equal(t, true, *iface.IPv6.IsPublic) assert.ElementsMatch(t, []string{"192.168.1.0/24"}, iface.IPRanges) } diff --git a/test/unit/interface_test.go b/test/unit/interface_test.go index 3bf92fc7d..fb5e5d2d3 100644 --- a/test/unit/interface_test.go +++ b/test/unit/interface_test.go @@ -4,10 +4,9 @@ import ( "context" "testing" - "github.com/stretchr/testify/require" - "github.com/linode/linodego" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestInterface_Get(t *testing.T) { @@ -139,7 +138,7 @@ func TestInterface_GetVPC(t *testing.T) { assert.Equal(t, "4321::/64", iface.VPC.IPv6.Ranges[0].Range) - assert.Equal(t, true, iface.VPC.IPv6.IsPublic) + assert.Equal(t, true, *iface.VPC.IPv6.IsPublic) } func TestInterface_CreatePublic(t *testing.T) { @@ -159,7 +158,7 @@ func TestInterface_CreatePublic(t *testing.T) { opts := linodego.LinodeInterfaceCreateOptions{ Public: &linodego.PublicInterfaceCreateOptions{ IPv4: &linodego.PublicInterfaceIPv4CreateOptions{ - Addresses: []linodego.PublicInterfaceIPv4AddressCreateOptions{ + Addresses: &[]linodego.PublicInterfaceIPv4AddressCreateOptions{ { Address: linodego.Pointer("auto"), Primary: linodego.Pointer(true), @@ -226,15 +225,15 @@ func TestInterface_UpdateVPC(t *testing.T) { IPv4: linodego.Pointer(true), IPv6: linodego.Pointer(true), }, - VPC: &linodego.VPCInterfaceCreateOptions{ + VPC: &linodego.VPCInterfaceUpdateOptions{ IPv4: &linodego.VPCInterfaceIPv4CreateOptions{ - Addresses: []linodego.VPCInterfaceIPv4AddressCreateOptions{ + Addresses: &[]linodego.VPCInterfaceIPv4AddressCreateOptions{ { Address: linodego.Pointer("192.168.23.4"), Primary: linodego.Pointer(true), }, }, - Ranges: []linodego.VPCInterfaceIPv4RangeCreateOptions{ + Ranges: &[]linodego.VPCInterfaceIPv4RangeCreateOptions{ { Range: "192.168.23.16/28", }, @@ -244,14 +243,14 @@ func TestInterface_UpdateVPC(t *testing.T) { }, }, IPv6: &linodego.VPCInterfaceIPv6CreateOptions{ - SLAAC: []linodego.VPCInterfaceIPv6SLAACCreateOptions{ + SLAAC: &[]linodego.VPCInterfaceIPv6SLAACCreateOptions{ { Range: "1235::/64", }, }, - Ranges: []linodego.VPCInterfaceIPv6RangeCreateOptions{ + Ranges: &[]linodego.VPCInterfaceIPv6RangeCreateOptions{ { - "4322::/64", + Range: "4322::/64", }, }, }, @@ -278,7 +277,7 @@ func TestInterface_UpdateVPC(t *testing.T) { assert.Equal(t, "4322::/64", iface.VPC.IPv6.Ranges[0].Range) - assert.Equal(t, false, iface.VPC.IPv6.IsPublic) + assert.Equal(t, false, *iface.VPC.IPv6.IsPublic) } func TestInterface_Upgrade(t *testing.T) { diff --git a/test/unit/lke_clusters_test.go b/test/unit/lke_clusters_test.go index 3303a6204..0dfad0d51 100644 --- a/test/unit/lke_clusters_test.go +++ b/test/unit/lke_clusters_test.go @@ -62,6 +62,10 @@ func TestLKECluster_List(t *testing.T) { assert.Equal(t, 123, clusters[0].ID) assert.Equal(t, "test-cluster", clusters[0].Label) assert.Equal(t, "us-east", clusters[0].Region) + assert.Equal(t, 123, clusters[0].SubnetID) + assert.Equal(t, 456, clusters[0].VpcID) + assert.Equal(t, linodego.LKEClusterStackIPv4, clusters[0].StackType) + assert.Equal(t, false, clusters[0].ControlPlane.AuditLogsEnabled) } func TestLKECluster_Get(t *testing.T) { @@ -78,6 +82,10 @@ func TestLKECluster_Get(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 123, cluster.ID) assert.Equal(t, "test-cluster", cluster.Label) + assert.Equal(t, 123, cluster.SubnetID) + assert.Equal(t, 456, cluster.VpcID) + assert.Equal(t, linodego.LKEClusterStackIPv4, cluster.StackType) + assert.Equal(t, false, cluster.ControlPlane.AuditLogsEnabled) } func TestLKECluster_Create(t *testing.T) { @@ -93,6 +101,12 @@ func TestLKECluster_Create(t *testing.T) { Region: "us-west", K8sVersion: "1.22", Tags: []string{"tag1"}, + SubnetID: linodego.Pointer(123), + VpcID: linodego.Pointer(456), + StackType: linodego.Pointer(linodego.LKEClusterStackIPv4), + ControlPlane: &linodego.LKEClusterControlPlaneOptions{ + AuditLogsEnabled: linodego.Pointer(false), + }, } base.MockPost("lke/clusters", fixtureData) @@ -101,6 +115,10 @@ func TestLKECluster_Create(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "new-cluster", cluster.Label) assert.Equal(t, "us-west", cluster.Region) + assert.Equal(t, 123, cluster.SubnetID) + assert.Equal(t, 456, cluster.VpcID) + assert.Equal(t, linodego.LKEClusterStackIPv4, cluster.StackType) + assert.Equal(t, false, cluster.ControlPlane.AuditLogsEnabled) } func TestLKECluster_Update(t *testing.T) { @@ -114,6 +132,9 @@ func TestLKECluster_Update(t *testing.T) { updateOptions := linodego.LKEClusterUpdateOptions{ Label: "updated-cluster", Tags: &[]string{"new-tag"}, + ControlPlane: &linodego.LKEClusterControlPlaneOptions{ + AuditLogsEnabled: linodego.Pointer(true), + }, } base.MockPut("lke/clusters/123", fixtureData) @@ -121,6 +142,7 @@ func TestLKECluster_Update(t *testing.T) { cluster, err := base.Client.UpdateLKECluster(context.Background(), 123, updateOptions) assert.NoError(t, err) assert.Equal(t, "updated-cluster", cluster.Label) + assert.Equal(t, true, cluster.ControlPlane.AuditLogsEnabled) } func TestLKECluster_Delete(t *testing.T) { diff --git a/test/unit/monitor_alert_definitions_test.go b/test/unit/monitor_alert_definitions_test.go new file mode 100644 index 000000000..dcfab3c30 --- /dev/null +++ b/test/unit/monitor_alert_definitions_test.go @@ -0,0 +1,178 @@ +package unit + +import ( + "context" + "encoding/json" + "testing" + + "github.com/linode/linodego" + "github.com/stretchr/testify/assert" +) + +const ( + testMonitorAlertDefinitionServiceType = "dbaas" + testMonitorAlertDefinitionID = 123 + + monitorAlertDefinitionGetResponse = `{ + "id": 123, + "label": "test-alert-definition", + "severity": 1, + "type": "some_type", + "service_type": "dbaas", + "status": "enabled", + "entity_ids": ["12345"], + "channel_ids": [1], + "is_enabled": true + }` + + monitorAlertDefinitionListResponse = `{ + "data": [{ + "id": 123, + "label": "test-alert-definition", + "severity": 1, + "type": "some_type", + "service_type": "dbaas", + "status": "enabled", + "entity_ids": ["12345"], + "channel_ids": [1], + "is_enabled": true + }], + "page": 1, + "pages": 1, + "results": 1 + }` + + monitorAlertDefinitionUpdateResponse = `{ + "id": 123, + "label": "test-alert-definition-renamed", + "severity": 2, + "type": "some_type", + "service_type": "dbaas", + "status": "disabled", + "entity_ids": ["12345"], + "channel_ids": [1, 2], + "is_enabled": false + }` + + monitorAlertDefinitionUpdateLabelOnlyResponseSingleLine = `{"id": 123, "label": "test-alert-definition-renamed-one-line", "severity": 1, "type": "some_type", "service_type": "dbaas", "status": "enabled", "entity_ids": ["12345"], "channel_ids": [1], "is_enabled": true}` +) + +func TestCreateMonitorAlertDefinition(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockPost("monitor/services/dbaas/alert-definitions", json.RawMessage(monitorAlertDefinitionGetResponse)) + + createOpts := linodego.AlertDefinitionCreateOptions{ + Label: "test-alert-definition", + Severity: int(linodego.SeverityLow), + ChannelIDs: []int{1}, + EntityIDs: []string{"12345"}, + } + + alert, err := base.Client.CreateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, createOpts) + assert.NoError(t, err) + assert.NotNil(t, alert) + assert.Equal(t, "test-alert-definition", alert.Label) + assert.Equal(t, testMonitorAlertDefinitionID, alert.ID) +} + +func TestCreateMonitorAlertDefinitionWithIdempotency(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockPost("monitor/services/dbaas/alert-definitions", json.RawMessage(monitorAlertDefinitionGetResponse)) + + createOpts := linodego.AlertDefinitionCreateOptions{ + Label: "test-alert-definition", + Severity: int(linodego.SeverityLow), + ChannelIDs: []int{1}, + EntityIDs: []string{"12345"}, + } + + alert, err := base.Client.CreateMonitorAlertDefinitionWithIdempotency(context.Background(), testMonitorAlertDefinitionServiceType, createOpts, "idempotency-key") + assert.NoError(t, err) + assert.NotNil(t, alert) + assert.Equal(t, "test-alert-definition", alert.Label) + assert.Equal(t, testMonitorAlertDefinitionID, alert.ID) +} + +func TestGetMonitorAlertDefinition(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockGet("monitor/services/dbaas/alert-definitions/123", json.RawMessage(monitorAlertDefinitionGetResponse)) + + alert, err := base.Client.GetMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, testMonitorAlertDefinitionID) + assert.NoError(t, err) + assert.NotNil(t, alert) + assert.Equal(t, "test-alert-definition", alert.Label) + assert.Equal(t, testMonitorAlertDefinitionID, alert.ID) +} + +func TestListMonitorAlertDefinitions(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockGet("monitor/services/dbaas/alert-definitions", json.RawMessage(monitorAlertDefinitionListResponse)) + + alerts, err := base.Client.ListMonitorAlertDefinitions(context.Background(), testMonitorAlertDefinitionServiceType, nil) + assert.NoError(t, err) + assert.Len(t, alerts, 1) + assert.Equal(t, "test-alert-definition", alerts[0].Label) + assert.Equal(t, testMonitorAlertDefinitionID, alerts[0].ID) +} + +func TestUpdateMonitorAlertDefinition(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockPut("monitor/services/dbaas/alert-definitions/123", json.RawMessage(monitorAlertDefinitionUpdateResponse)) + + updateOpts := linodego.AlertDefinitionUpdateOptions{ + Label: "test-alert-definition-renamed", + Severity: int(linodego.SeverityLow), + ChannelIDs: []int{1, 2}, + } + + alert, err := base.Client.UpdateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, testMonitorAlertDefinitionID, updateOpts) + assert.NoError(t, err) + assert.NotNil(t, alert) + assert.Equal(t, "test-alert-definition-renamed", alert.Label) + assert.Equal(t, 2, alert.Severity) +} + +func TestUpdateMonitorAlertDefinition_LabelOnly(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + // Mock a PUT that returns the single-line fixture + base.MockPut("monitor/services/dbaas/alert-definitions/123", json.RawMessage(monitorAlertDefinitionUpdateLabelOnlyResponseSingleLine)) + + updateOpts := linodego.AlertDefinitionUpdateOptions{ + Label: "test-alert-definition-renamed-one-line", + } + + alert, err := base.Client.UpdateMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, testMonitorAlertDefinitionID, updateOpts) + assert.NoError(t, err) + assert.NotNil(t, alert) + assert.Equal(t, "test-alert-definition-renamed-one-line", alert.Label) + assert.Equal(t, testMonitorAlertDefinitionID, alert.ID) +} + +func TestDeleteMonitorAlertDefinition(t *testing.T) { + var base ClientBaseCase + base.SetUp(t) + defer base.TearDown(t) + + base.MockDelete("monitor/services/dbaas/alert-definitions/123", nil) + + err := base.Client.DeleteMonitorAlertDefinition(context.Background(), testMonitorAlertDefinitionServiceType, testMonitorAlertDefinitionID) + assert.NoError(t, err) +} diff --git a/test/unit/vpc_test.go b/test/unit/vpc_test.go index a68f2a36d..8b82a932e 100644 --- a/test/unit/vpc_test.go +++ b/test/unit/vpc_test.go @@ -5,10 +5,9 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" - "github.com/linode/linodego" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestVPC_Create(t *testing.T) { diff --git a/volumes.go b/volumes.go index 70fe3da94..820b0f460 100644 --- a/volumes.go +++ b/volumes.go @@ -99,7 +99,7 @@ func (v Volume) GetUpdateOptions() (updateOpts VolumeUpdateOptions) { updateOpts.Label = v.Label updateOpts.Tags = &v.Tags - return + return updateOpts } // GetCreateOptions converts a Volume to VolumeCreateOptions for use in CreateVolume @@ -113,7 +113,7 @@ func (v Volume) GetCreateOptions() (createOpts VolumeCreateOptions) { createOpts.LinodeID = *v.LinodeID } - return + return createOpts } // ListVolumes lists Volumes