diff --git a/account_events.go b/account_events.go index 5f88fe5d7..7ea8dd8ad 100644 --- a/account_events.go +++ b/account_events.go @@ -158,6 +158,13 @@ const ( ActionPaymentMethodAdd EventAction = "payment_method_add" ActionPaymentSubmitted EventAction = "payment_submitted" ActionPasswordReset EventAction = "password_reset" + ActionPlacementGroupCreate EventAction = "placement_group_create" + ActionPlacementGroupUpdate EventAction = "placement_group_update" + ActionPlacementGroupDelete EventAction = "placement_group_delete" + ActionPlacementGroupAssign EventAction = "placement_group_assign" + ActionPlacementGroupUnassign EventAction = "placement_group_unassign" + ActionPlacementGroupBecameNonCompliant EventAction = "placement_group_became_non_compliant" + ActionPlacementGroupBecameCompliant EventAction = "placement_group_became_compliant" ActionProfileUpdate EventAction = "profile_update" ActionStackScriptCreate EventAction = "stackscript_create" ActionStackScriptDelete EventAction = "stackscript_delete" @@ -226,6 +233,7 @@ const ( EntityManagedService EntityType = "managed_service" EntityNodebalancer EntityType = "nodebalancer" EntityOAuthClient EntityType = "oauth_client" + EntityPlacementGroup EntityType = "placement_group" EntityProfile EntityType = "profile" EntityStackscript EntityType = "stackscript" EntityTag EntityType = "tag" diff --git a/go.work.sum b/go.work.sum index 6c1d40342..e69de29bb 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,46 +0,0 @@ -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= -github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5 h1:8Q0qkMVC/MmWkpIdlvZgcv2o2jrlF6zqVOh7W5YHdMA= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= -k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= diff --git a/instances.go b/instances.go index a56c3321b..402981050 100644 --- a/instances.go +++ b/instances.go @@ -62,6 +62,9 @@ type Instance struct { Specs *InstanceSpec `json:"specs"` WatchdogEnabled bool `json:"watchdog_enabled"` Tags []string `json:"tags"` + + // NOTE: Placement Groups may not currently be available to all users. + PlacementGroup *InstancePlacementGroup `json:"placement_group"` } // InstanceSpec represents a linode spec @@ -104,6 +107,15 @@ type InstanceTransfer struct { Quota int `json:"quota"` } +// InstancePlacementGroup represents information about the placement group +// this Linode is a part of. +type InstancePlacementGroup struct { + ID int `json:"id"` + Label string `json:"label"` + AffinityType PlacementGroupAffinityType `json:"affinity_type"` + IsStrict bool `json:"is_strict"` +} + // InstanceMetadataOptions specifies various Instance creation fields // that relate to the Linode Metadata service. type InstanceMetadataOptions struct { @@ -130,6 +142,9 @@ type InstanceCreateOptions struct { Metadata *InstanceMetadataOptions `json:"metadata,omitempty"` FirewallID int `json:"firewall_id,omitempty"` + // NOTE: Placement Groups may not currently be available to all users. + PlacementGroup *InstanceCreatePlacementGroupOptions `json:"placement_group,omitempty"` + // Creation fields that need to be set explicitly false, "", or 0 use pointers SwapSize *int `json:"swap_size,omitempty"` Booted *bool `json:"booted,omitempty"` @@ -138,6 +153,13 @@ type InstanceCreateOptions struct { Group string `json:"group,omitempty"` } +// InstanceCreatePlacementGroupOptions represents the placement group +// to create this Linode under. +type InstanceCreatePlacementGroupOptions struct { + ID int `json:"id"` + CompliantOnly *bool `json:"compliant_only,omitempty"` +} + // InstanceUpdateOptions is an options struct used when Updating an Instance type InstanceUpdateOptions struct { Label string `json:"label,omitempty"` @@ -190,13 +212,14 @@ type InstanceCloneOptions struct { Type string `json:"type,omitempty"` // LinodeID is an optional existing instance to use as the target of the clone - LinodeID int `json:"linode_id,omitempty"` - Label string `json:"label,omitempty"` - BackupsEnabled bool `json:"backups_enabled"` - Disks []int `json:"disks,omitempty"` - Configs []int `json:"configs,omitempty"` - PrivateIP bool `json:"private_ip,omitempty"` - Metadata *InstanceMetadataOptions `json:"metadata,omitempty"` + LinodeID int `json:"linode_id,omitempty"` + Label string `json:"label,omitempty"` + BackupsEnabled bool `json:"backups_enabled"` + Disks []int `json:"disks,omitempty"` + Configs []int `json:"configs,omitempty"` + PrivateIP bool `json:"private_ip,omitempty"` + Metadata *InstanceMetadataOptions `json:"metadata,omitempty"` + PlacementGroup *InstanceCreatePlacementGroupOptions `json:"placement_group,omitempty"` // Deprecated: group is a deprecated property denoting a group label for the Linode. Group string `json:"group,omitempty"` @@ -211,10 +234,12 @@ type InstanceResizeOptions struct { AllowAutoDiskResize *bool `json:"allow_auto_disk_resize,omitempty"` } -// InstanceResizeOptions is an options struct used when resizing an instance +// InstanceMigrateOptions is an options struct used when migrating an instance type InstanceMigrateOptions struct { Type InstanceMigrationType `json:"type,omitempty"` Region string `json:"region,omitempty"` + + PlacementGroup *InstanceCreatePlacementGroupOptions `json:"placement_group,omitempty"` } // InstancesPagedResponse represents a linode API response for listing diff --git a/placement_groups.go b/placement_groups.go new file mode 100644 index 000000000..3987adca9 --- /dev/null +++ b/placement_groups.go @@ -0,0 +1,160 @@ +package linodego + +import "context" + +// PlacementGroupAffinityType is an enum that determines the affinity policy +// for Linodes in a placement group. +type PlacementGroupAffinityType string + +const ( + AffinityTypeAntiAffinityLocal PlacementGroupAffinityType = "anti_affinity:local" +) + +// PlacementGroupMember represents a single Linode assigned to a +// placement group. +type PlacementGroupMember struct { + LinodeID int `json:"linode_id"` + IsCompliant bool `json:"is_compliant"` +} + +// PlacementGroup represents a Linode placement group. +// NOTE: Placement Groups may not currently be available to all users. +type PlacementGroup struct { + ID int `json:"id"` + Label string `json:"label"` + Region string `json:"region"` + AffinityType PlacementGroupAffinityType `json:"affinity_type"` + IsCompliant bool `json:"is_compliant"` + IsStrict bool `json:"is_strict"` + Members []PlacementGroupMember `json:"members"` +} + +// PlacementGroupCreateOptions represents the options to use +// when creating a placement group. +type PlacementGroupCreateOptions struct { + Label string `json:"label"` + Region string `json:"region"` + AffinityType PlacementGroupAffinityType `json:"affinity_type"` + IsStrict bool `json:"is_strict"` +} + +// PlacementGroupUpdateOptions represents the options to use +// when updating a placement group. +type PlacementGroupUpdateOptions struct { + Label string `json:"label,omitempty"` +} + +// PlacementGroupAssignOptions represents options used when +// assigning Linodes to a placement group. +type PlacementGroupAssignOptions struct { + Linodes []int `json:"linodes"` + CompliantOnly *bool `json:"compliant_only,omitempty"` +} + +// PlacementGroupUnAssignOptions represents options used when +// unassigning Linodes from a placement group. +type PlacementGroupUnAssignOptions struct { + Linodes []int `json:"linodes"` +} + +// ListPlacementGroups lists placement groups under the current account +// matching the given list options. +// NOTE: Placement Groups may not currently be available to all users. +func (c *Client) ListPlacementGroups( + ctx context.Context, + options *ListOptions, +) ([]PlacementGroup, error) { + return getPaginatedResults[PlacementGroup]( + ctx, + c, + "placement/groups", + options, + ) +} + +// GetPlacementGroup gets a placement group with the specified ID. +// NOTE: Placement Groups may not currently be available to all users. +func (c *Client) GetPlacementGroup( + ctx context.Context, + id int, +) (*PlacementGroup, error) { + return doGETRequest[PlacementGroup]( + ctx, + c, + formatAPIPath("placement/groups/%d", id), + ) +} + +// CreatePlacementGroup creates a placement group with the specified options. +// NOTE: Placement Groups may not currently be available to all users. +func (c *Client) CreatePlacementGroup( + ctx context.Context, + options PlacementGroupCreateOptions, +) (*PlacementGroup, error) { + return doPOSTRequest[PlacementGroup]( + ctx, + c, + "placement/groups", + options, + ) +} + +// UpdatePlacementGroup updates a placement group with the specified ID using the provided options. +// NOTE: Placement Groups may not currently be available to all users. +func (c *Client) UpdatePlacementGroup( + ctx context.Context, + id int, + options PlacementGroupUpdateOptions, +) (*PlacementGroup, error) { + return doPUTRequest[PlacementGroup]( + ctx, + c, + formatAPIPath("placement/groups/%d", id), + options, + ) +} + +// AssignPlacementGroupLinodes assigns the specified Linodes to the given +// placement group. +// NOTE: Placement Groups may not currently be available to all users. +func (c *Client) AssignPlacementGroupLinodes( + ctx context.Context, + id int, + options PlacementGroupAssignOptions, +) (*PlacementGroup, error) { + return doPOSTRequest[PlacementGroup]( + ctx, + c, + formatAPIPath("placement/groups/%d/assign", id), + options, + ) +} + +// UnassignPlacementGroupLinodes un-assigns the specified Linodes from the given +// placement group. +// NOTE: Placement Groups may not currently be available to all users. +func (c *Client) UnassignPlacementGroupLinodes( + ctx context.Context, + id int, + options PlacementGroupUnAssignOptions, +) (*PlacementGroup, error) { + return doPOSTRequest[PlacementGroup]( + ctx, + c, + formatAPIPath("placement/groups/%d/unassign", id), + options, + ) +} + +// DeletePlacementGroup deletes a placement group with the specified ID. +// NOTE: Placement Groups may not currently be available to all users. +func (c *Client) DeletePlacementGroup( + ctx context.Context, + id int, +) error { + return doDELETERequest( + ctx, + c, + formatAPIPath("placement/groups/%d", id), + ) +} diff --git a/regions.go b/regions.go index cecc8ea14..486f0491c 100644 --- a/regions.go +++ b/regions.go @@ -53,10 +53,12 @@ type Region struct { // A List of enums from the above constants Capabilities []string `json:"capabilities"` - Status string `json:"status"` - Resolvers RegionResolvers `json:"resolvers"` - Label string `json:"label"` - SiteType string `json:"site_type"` + Status string `json:"status"` + Label string `json:"label"` + SiteType string `json:"site_type"` + + Resolvers RegionResolvers `json:"resolvers"` + PlacementGroupLimits *RegionPlacementGroupLimits `json:"placement_group_limits"` } // RegionResolvers contains the DNS resolvers of a region @@ -65,6 +67,13 @@ type RegionResolvers struct { IPv6 string `json:"ipv6"` } +// RegionPlacementGroupLimits contains information about the +// placement group limits for the current user in the current region. +type RegionPlacementGroupLimits struct { + MaximumPGsPerCustomer int `json:"maximum_pgs_per_customer"` + MaximumLinodesPerPG int `json:"maximum_linodes_per_pg"` +} + // RegionsPagedResponse represents a linode API response for listing type RegionsPagedResponse struct { *PageOptions diff --git a/test/integration/fixtures/TestInstance_withPG.yaml b/test/integration/fixtures/TestInstance_withPG.yaml new file mode 100644 index 000000000..bc7f63acc --- /dev/null +++ b/test/integration/fixtures/TestInstance_withPG.yaml @@ -0,0 +1,357 @@ +--- +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 + method: GET + response: + body: '{"data": [{"id": "ap-west", "label": "Mumbai, India", "country": "in", + "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", "VPCs", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ca-central", "label": "Toronto, Ontario, CAN", + "country": "ca", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", + "VPCs", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-southeast", "label": "Sydney, NSW, Australia", + "country": "au", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", + "VPCs", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "fake-cph-4", "label": "Fake CPH 4, DK", "country": + "dk", "capabilities": ["Linodes", "Metadata"], "status": "ok", "resolvers": + {"ipv4": "8.8.8.8,1.1.1.1,1.0.0.1,8.8.4.4", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "fake-cph-5", "label": "Fake CPH 5, DK", "country": + "dk", "capabilities": ["Linodes", "Metadata"], "status": "ok", "resolvers": + {"ipv4": "8.8.8.8,1.1.1.1,1.0.0.1,8.8.4.4", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-central", "label": "Dallas, TX, USA", "country": + "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Kubernetes", + "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-west", "label": "Fremont, CA, USA", "country": + "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-southeast", "label": "Atlanta, GA, USA", + "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-east", "label": "Newark, NJ, USA", "country": + "us", "capabilities": ["Linodes", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", + "Bare Metal", "Vlans", "VPCs", "Block Storage Migrations", "Managed Databases", + "Placement Group"], "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,207.192.69.4,207.192.69.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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "eu-west", "label": "London, England, UK", + "country": "uk", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Placement Group"], + "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-south", "label": "Singapore, SG", "country": + "sg", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Managed Databases"], + "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "eu-central", "label": "Frankfurt, DE", "country": + "de", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-northeast", "label": "Tokyo 2, JP", "country": + "jp", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}], "page": 1, "pages": 1, "results": 13}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=900 + - private, max-age=60, s-maxage=60 + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Accept-Encoding + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - '*' + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"label":"linodego-test-1718723007764225000","region":"us-east","affinity_type":"anti_affinity:local","is_strict":false}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/placement/groups + method: POST + response: + body: '{"id": 4021, "label": "linodego-test-1718723007764225000", "region": "us-east", + "affinity_type": "anti_affinity:local", "is_strict": false, "is_compliant": + true, "members": []}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Length: + - "176" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"region":"us-east","type":"g6-nanode-1","label":"go-test-ins-5skg99rf1a38","root_pass":"Jb\u00262S#q7072\u003cXj;imqjH7zr5n96A)OVLK8j\\Z31@{+Ei0R72jG]\u003cEGbr.5F/=-\u0026l","image":"linode/debian9","firewall_id":34216,"placement_group":{"id":4021},"booted":false}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/linode/instances + method: POST + response: + body: '{"id": 25195351, "label": "go-test-ins-5skg99rf1a38", "group": "", "status": + "provisioning", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", + "type": "g6-nanode-1", "ipv4": ["143.42.181.72"], "ipv6": "1234::5678/128", + "image": "linode/debian9", "region": "us-east", "specs": {"disk": 25600, "memory": + 1024, "vcpus": 1, "gpus": 0, "transfer": 1000}, "alerts": {"cpu": 90, "network_in": + 10, "network_out": 10, "transfer_quota": 80, "io": 10000}, "backups": {"enabled": + false, "available": false, "schedule": {"day": null, "window": null}, "last_successful": + null}, "hypervisor": "kvm", "watchdog_enabled": true, "tags": [], "host_uuid": + "2e8c6f5e2c38ef3261c570ae22ac2b65435af20b", "has_user_data": false, "placement_group": + {"id": 4021, "label": "linodego-test-1718723007764225000", "affinity_type": + "anti_affinity:local", "is_strict": false}, "disk_encryption": "enabled", "lke_cluster_id": + null}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Accept-Encoding + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "10" + 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/linode/instances/25195351 + 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 + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Length: + - "2" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + 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/placement/groups/4021 + 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 + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Length: + - "2" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" diff --git a/test/integration/fixtures/TestPlacementGroup_assignment.yaml b/test/integration/fixtures/TestPlacementGroup_assignment.yaml new file mode 100644 index 000000000..449cbc77a --- /dev/null +++ b/test/integration/fixtures/TestPlacementGroup_assignment.yaml @@ -0,0 +1,481 @@ +--- +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 + method: GET + response: + body: '{"data": [{"id": "ap-west", "label": "Mumbai, India", "country": "in", + "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", "VPCs", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ca-central", "label": "Toronto, Ontario, CAN", + "country": "ca", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", + "VPCs", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-southeast", "label": "Sydney, NSW, Australia", + "country": "au", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", + "VPCs", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "fake-cph-4", "label": "Fake CPH 4, DK", "country": + "dk", "capabilities": ["Linodes", "Metadata"], "status": "ok", "resolvers": + {"ipv4": "8.8.8.8,1.1.1.1,1.0.0.1,8.8.4.4", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "fake-cph-5", "label": "Fake CPH 5, DK", "country": + "dk", "capabilities": ["Linodes", "Metadata"], "status": "ok", "resolvers": + {"ipv4": "8.8.8.8,1.1.1.1,1.0.0.1,8.8.4.4", "ipv6": "1234::5678,1234::5678,1234::5678,1234::5678"}, + "placement_group_limits": {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-central", "label": "Dallas, TX, USA", "country": + "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Kubernetes", + "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-west", "label": "Fremont, CA, USA", "country": + "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-southeast", "label": "Atlanta, GA, USA", + "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-east", "label": "Newark, NJ, USA", "country": + "us", "capabilities": ["Linodes", "Disk Encryption", "Backups", "NodeBalancers", + "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", + "Bare Metal", "Vlans", "VPCs", "Block Storage Migrations", "Managed Databases", + "Placement Group"], "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,207.192.69.4,207.192.69.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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "eu-west", "label": "London, England, UK", + "country": "uk", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Placement Group"], + "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-south", "label": "Singapore, SG", "country": + "sg", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Managed Databases"], + "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "eu-central", "label": "Frankfurt, DE", "country": + "de", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-northeast", "label": "Tokyo 2, JP", "country": + "jp", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 100, "maximum_linodes_per_pg": + 5}, "site_type": "core"}], "page": 1, "pages": 1, "results": 13}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=900 + - private, max-age=60, s-maxage=60 + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Accept-Encoding + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - '*' + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"label":"linodego-test-1718722972576732000","region":"us-east","affinity_type":"anti_affinity:local","is_strict":false}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/placement/groups + method: POST + response: + body: '{"id": 4020, "label": "linodego-test-1718722972576732000", "region": "us-east", + "affinity_type": "anti_affinity:local", "is_strict": false, "is_compliant": + true, "members": []}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Length: + - "176" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"region":"us-east","type":"g6-nanode-1","label":"go-test-ins-t02660a1ehag","root_pass":"1@L\u003e)0sb6qd`]5\u0026OW~K\\Q6F~5l4Tys\\3Gh9RI93}2Wrs6u;Pu?5c;~)ERj8Jm9Xo","image":"linode/debian9","firewall_id":34215,"booted":false}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/linode/instances + method: POST + response: + body: '{"id": 25195349, "label": "go-test-ins-t02660a1ehag", "group": "", "status": + "provisioning", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", + "type": "g6-nanode-1", "ipv4": ["143.42.181.254"], "ipv6": "1234::5678/128", + "image": "linode/debian9", "region": "us-east", "specs": {"disk": 25600, "memory": + 1024, "vcpus": 1, "gpus": 0, "transfer": 1000}, "alerts": {"cpu": 90, "network_in": + 10, "network_out": 10, "transfer_quota": 80, "io": 10000}, "backups": {"enabled": + false, "available": false, "schedule": {"day": null, "window": null}, "last_successful": + null}, "hypervisor": "kvm", "watchdog_enabled": true, "tags": [], "host_uuid": + "53257e56e7692e143036a5fbf7daaf92b5350bf3", "has_user_data": false, "placement_group": + null, "disk_encryption": "enabled", "lke_cluster_id": null}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Accept-Encoding + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "10" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"linodes":[25195349]}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/placement/groups/4020/assign + method: POST + response: + body: '{"id": 4020, "label": "linodego-test-1718722972576732000", "region": "us-east", + "affinity_type": "anti_affinity:local", "is_strict": false, "is_compliant": + true, "members": [{"linode_id": 25195349, "is_compliant": true}]}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Length: + - "221" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + 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/linode/instances/25195349 + method: GET + response: + body: '{"id": 25195349, "label": "go-test-ins-t02660a1ehag", "group": "", "status": + "provisioning", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", + "type": "g6-nanode-1", "ipv4": ["143.42.181.254"], "ipv6": "1234::5678/128", + "image": "linode/debian9", "region": "us-east", "specs": {"disk": 25600, "memory": + 1024, "vcpus": 1, "gpus": 0, "transfer": 1000}, "alerts": {"cpu": 90, "network_in": + 10, "network_out": 10, "transfer_quota": 80, "io": 10000}, "backups": {"enabled": + false, "available": false, "schedule": {"day": null, "window": null}, "last_successful": + null}, "hypervisor": "kvm", "watchdog_enabled": true, "tags": [], "host_uuid": + "53257e56e7692e143036a5fbf7daaf92b5350bf3", "has_user_data": false, "placement_group": + {"id": 4020, "label": "linodego-test-1718722972576732000", "affinity_type": + "anti_affinity:local", "is_strict": false}, "disk_encryption": "enabled", "lke_cluster_id": + null}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=0, s-maxage=0, no-cache, no-store + - private, max-age=60, s-maxage=60 + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Accept-Encoding + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_only + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"linodes":[25195349]}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/placement/groups/4020/unassign + method: POST + response: + body: '{"id": 4020, "label": "linodego-test-1718722972576732000", "region": "us-east", + "affinity_type": "anti_affinity:local", "is_strict": false, "is_compliant": + true, "members": []}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Length: + - "176" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + 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/placement/groups/4020 + 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 + Cache-Control: + - private, max-age=60, s-maxage=60 + Content-Length: + - "2" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" diff --git a/test/integration/fixtures/TestPlacementGroup_basic.yaml b/test/integration/fixtures/TestPlacementGroup_basic.yaml new file mode 100644 index 000000000..28a50ca90 --- /dev/null +++ b/test/integration/fixtures/TestPlacementGroup_basic.yaml @@ -0,0 +1,419 @@ +--- +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 + method: GET + response: + body: '{"data": [{"id": "ap-west", "label": "Mumbai, India", "country": "in", + "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", "VPCs", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ca-central", "label": "Toronto, Ontario, CAN", + "country": "ca", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", + "VPCs", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-southeast", "label": "Sydney, NSW, Australia", + "country": "au", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", + "VPCs", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-central", "label": "Dallas, TX, USA", "country": + "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Kubernetes", + "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-west", "label": "Fremont, CA, USA", "country": + "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-southeast", "label": "Atlanta, GA, USA", + "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-east", "label": "Newark, NJ, USA", "country": + "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Block Storage", + "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Bare Metal", + "Vlans", "VPCs", "Block Storage Migrations", "Managed Databases", "Metadata"], + "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,207.192.69.4,207.192.69.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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "eu-west", "label": "London, England, UK", + "country": "uk", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Placement Group"], + "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-south", "label": "Singapore, SG", "country": + "sg", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Object Storage", + "Managed Databases"], "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "eu-central", "label": "Frankfurt, DE", "country": + "de", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Object Storage", + "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-northeast", "label": "Tokyo 2, JP", "country": + "jp", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}], "page": 1, "pages": 1, "results": 11}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=900 + - private, max-age=60, s-maxage=60 + Connection: + - keep-alive + Content-Length: + - "7144" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - '*' + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"label":"linodego-test-1712862362503240000","region":"eu-west","affinity_type":"anti_affinity:local","is_strict":false}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/placement/groups + method: POST + response: + body: '{"id": 351, "label": "linodego-test-1712862362503240000", "region": "eu-west", + "affinity_type": "anti_affinity:local", "is_strict": false, "is_compliant": + true, "members": []}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Connection: + - keep-alive + Content-Length: + - "175" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" +- request: + body: '{"label":"linodego-test-1712862362503240000-updated"}' + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + User-Agent: + - linodego/dev https://github.com/linode/linodego + url: https://api.linode.com/v4beta/placement/groups/351 + method: PUT + response: + body: '{"id": 351, "label": "linodego-test-1712862362503240000-updated", "region": + "eu-west", "affinity_type": "anti_affinity:local", "is_strict": false, "is_compliant": + true, "members": []}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=60, s-maxage=60 + Connection: + - keep-alive + Content-Length: + - "183" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + 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/placement/groups/351 + method: GET + response: + body: '{"id": 351, "label": "linodego-test-1712862362503240000-updated", "region": + "eu-west", "affinity_type": "anti_affinity:local", "is_strict": false, "is_compliant": + true, "members": []}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=0, s-maxage=0, no-cache, no-store + - private, max-age=60, s-maxage=60 + Connection: + - keep-alive + Content-Length: + - "183" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_only + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + 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 + X-Filter: + - '{"id": 351}' + url: https://api.linode.com/v4beta/placement/groups?page=1 + method: GET + response: + body: '{"data": [{"id": 351, "label": "linodego-test-1712862362503240000-updated", + "region": "eu-west", "affinity_type": "anti_affinity:local", "is_strict": false, + "is_compliant": true, "members": []}], "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 + Cache-Control: + - private, max-age=0, s-maxage=0, no-cache, no-store + - private, max-age=60, s-maxage=60 + Connection: + - keep-alive + Content-Length: + - "232" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_only + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + 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/placement/groups/351 + 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 + Cache-Control: + - private, max-age=60, s-maxage=60 + Connection: + - keep-alive + Content-Length: + - "2" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - linodes:read_write + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" diff --git a/test/integration/fixtures/TestRegions_pgLimits.yaml b/test/integration/fixtures/TestRegions_pgLimits.yaml new file mode 100644 index 000000000..d238f4085 --- /dev/null +++ b/test/integration/fixtures/TestRegions_pgLimits.yaml @@ -0,0 +1,120 @@ +--- +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 + method: GET + response: + body: '{"data": [{"id": "ap-west", "label": "Mumbai, India", "country": "in", + "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", "VPCs", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ca-central", "label": "Toronto, Ontario, CAN", + "country": "ca", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", + "VPCs", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-southeast", "label": "Sydney, NSW, Australia", + "country": "au", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Vlans", + "VPCs", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-central", "label": "Dallas, TX, USA", "country": + "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Kubernetes", + "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-west", "label": "Fremont, CA, USA", "country": + "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-southeast", "label": "Atlanta, GA, USA", + "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "us-east", "label": "Newark, NJ, USA", "country": + "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Block Storage", + "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Bare Metal", + "Vlans", "VPCs", "Block Storage Migrations", "Managed Databases", "Metadata"], + "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,207.192.69.4,207.192.69.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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "eu-west", "label": "London, England, UK", + "country": "uk", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Cloud + Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Placement Group"], + "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-south", "label": "Singapore, SG", "country": + "sg", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Object Storage", + "Managed Databases"], "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "eu-central", "label": "Frankfurt, DE", "country": + "de", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Object Storage", + "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}, {"id": "ap-northeast", "label": "Tokyo 2, JP", "country": + "jp", "capabilities": ["Linodes", "Backups", "NodeBalancers", "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": 5, "maximum_linodes_per_pg": + 5}, "site_type": "core"}], "page": 1, "pages": 1, "results": 11}' + headers: + Access-Control-Allow-Credentials: + - "true" + Access-Control-Allow-Headers: + - Authorization, Origin, X-Requested-With, Content-Type, Accept, X-Filter + Access-Control-Allow-Methods: + - HEAD, GET, OPTIONS, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Access-Control-Expose-Headers: + - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status + Cache-Control: + - private, max-age=900 + - private, max-age=60, s-maxage=60 + Connection: + - keep-alive + Content-Length: + - "7144" + Content-Security-Policy: + - default-src 'none' + Content-Type: + - application/json + Server: + - nginx + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization, X-Filter + - Authorization, X-Filter + X-Accepted-Oauth-Scopes: + - '*' + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + - DENY + X-Oauth-Scopes: + - '*' + X-Ratelimit-Limit: + - "400" + X-Xss-Protection: + - 1; mode=block + status: 200 OK + code: 200 + duration: "" diff --git a/test/integration/instances_test.go b/test/integration/instances_test.go index 91f6b9f68..061c9dadb 100644 --- a/test/integration/instances_test.go +++ b/test/integration/instances_test.go @@ -6,6 +6,8 @@ import ( "strconv" "testing" + "github.com/stretchr/testify/require" + "github.com/linode/linodego" ) @@ -475,6 +477,34 @@ func TestInstance_withMetadata(t *testing.T) { } } +func TestInstance_withPG(t *testing.T) { + client, clientTeardown := createTestClient(t, "fixtures/TestInstance_withPG") + + pg, pgTeardown, err := createPlacementGroup(t, client) + require.NoError(t, err) + + // Create an instance to assign to the PG + inst, err := createInstance(t, client, true, func(client *linodego.Client, options *linodego.InstanceCreateOptions) { + options.Region = pg.Region + options.PlacementGroup = &linodego.InstanceCreatePlacementGroupOptions{ + ID: pg.ID, + } + }) + require.NoError(t, err) + + defer func() { + client.DeleteInstance(context.Background(), inst.ID) + pgTeardown() + clientTeardown() + }() + + require.NotNil(t, inst.PlacementGroup) + require.Equal(t, inst.PlacementGroup.ID, pg.ID) + require.Equal(t, inst.PlacementGroup.Label, pg.Label) + require.Equal(t, inst.PlacementGroup.AffinityType, pg.AffinityType) + require.Equal(t, inst.PlacementGroup.IsStrict, pg.IsStrict) +} + func createInstance(t *testing.T, client *linodego.Client, enableCloudFirewall bool, modifiers ...instanceModifier) (*linodego.Instance, error) { if t != nil { t.Helper() diff --git a/test/integration/placement_group_test.go b/test/integration/placement_group_test.go new file mode 100644 index 000000000..7507f77ff --- /dev/null +++ b/test/integration/placement_group_test.go @@ -0,0 +1,145 @@ +package integration + +import ( + "context" + "fmt" + "reflect" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/linode/linodego" +) + +type placementGroupModifier func(*linodego.Client, *linodego.PlacementGroupCreateOptions) + +func TestPlacementGroup_basic(t *testing.T) { + client, clientTeardown := createTestClient(t, "fixtures/TestPlacementGroup_basic") + + // Create a PG + pg, pgTeardown, err := createPlacementGroup(t, client) + require.NoError(t, err) + + defer func() { + pgTeardown() + clientTeardown() + }() + + require.NotEqual(t, pg.ID, 0) + require.Contains(t, pg.Label, "linodego-test-") + require.NotEmpty(t, pg.Label) + require.Equal(t, pg.AffinityType, linodego.AffinityTypeAntiAffinityLocal) + require.Equal(t, pg.IsStrict, false) + require.Len(t, pg.Members, 0) + + updatedLabel := pg.Label + "-updated" + + // Test that the PG can be updated + pg, err = client.UpdatePlacementGroup( + context.Background(), + pg.ID, + linodego.PlacementGroupUpdateOptions{ + Label: updatedLabel, + }, + ) + require.NoError(t, err) + require.Equal(t, pg.Label, updatedLabel) + + // Test that the PG can be retrieved from the get endpoint + refreshedPG, err := client.GetPlacementGroup(context.Background(), pg.ID) + require.NoError(t, err) + require.True(t, reflect.DeepEqual(refreshedPG, pg)) + + // Test that the PG can be retrieved from the list endpoint + listedPGs, err := client.ListPlacementGroups(context.Background(), &linodego.ListOptions{ + Filter: fmt.Sprintf("{\"id\": %d}", pg.ID), + }) + require.NoError(t, err) + require.NotEmpty(t, listedPGs) + require.True(t, reflect.DeepEqual(listedPGs[0], *pg)) +} + +func TestPlacementGroup_assignment(t *testing.T) { + client, clientTeardown := createTestClient(t, "fixtures/TestPlacementGroup_assignment") + + pg, pgTeardown, err := createPlacementGroup(t, client) + require.NoError(t, err) + + // Create an instance to assign to the PG + inst, err := createInstance(t, client, true, func(client *linodego.Client, options *linodego.InstanceCreateOptions) { + options.Region = pg.Region + }) + require.NoError(t, err) + + defer func() { + // client.DeleteInstance(context.Background(), inst.ID) + pgTeardown() + clientTeardown() + }() + + // Ensure assignment works as expected + pg, err = client.AssignPlacementGroupLinodes( + context.Background(), + pg.ID, + linodego.PlacementGroupAssignOptions{ + Linodes: []int{ + inst.ID, + }, + }, + ) + require.NoError(t, err) + require.Len(t, pg.Members, 1) + require.Equal(t, pg.Members[0].LinodeID, inst.ID) + + // Refresh the instance to ensure the assignment has completed + inst, err = client.GetInstance(context.Background(), inst.ID) + require.NoError(t, err) + require.NotNil(t, inst.PlacementGroup) + require.Equal(t, inst.PlacementGroup.ID, pg.ID) + require.Equal(t, inst.PlacementGroup.Label, pg.Label) + require.Equal(t, inst.PlacementGroup.IsStrict, pg.IsStrict) + require.Equal(t, inst.PlacementGroup.AffinityType, pg.AffinityType) + + // Ensure unassignment works as expected + pg, err = client.UnassignPlacementGroupLinodes( + context.Background(), + pg.ID, + linodego.PlacementGroupUnAssignOptions{ + Linodes: []int{ + inst.ID, + }, + }, + ) + require.NoError(t, err) + require.Len(t, pg.Members, 0) +} + +func createPlacementGroup( + t *testing.T, + client *linodego.Client, + pgModifier ...placementGroupModifier, +) (*linodego.PlacementGroup, func(), error) { + t.Helper() + createOpts := linodego.PlacementGroupCreateOptions{ + Label: "linodego-test-" + getUniqueText(), + Region: getRegionsWithCaps(t, client, []string{"Placement Group"})[0], + AffinityType: linodego.AffinityTypeAntiAffinityLocal, + IsStrict: false, + } + + for _, mod := range pgModifier { + mod(client, &createOpts) + } + + pg, err := client.CreatePlacementGroup(context.Background(), createOpts) + if err != nil { + t.Fatalf("failed to create placement group: %s", err) + } + + teardown := func() { + if err := client.DeletePlacementGroup(context.Background(), pg.ID); err != nil { + t.Errorf("failed to delete placement group: %s", err) + } + } + return pg, teardown, err +} diff --git a/test/integration/regions_test.go b/test/integration/regions_test.go index 2ace12e1c..b4de36985 100644 --- a/test/integration/regions_test.go +++ b/test/integration/regions_test.go @@ -2,7 +2,11 @@ package integration import ( "context" + "slices" "testing" + + "github.com/linode/linodego" + "github.com/stretchr/testify/require" ) func TestRegions_List(t *testing.T) { @@ -21,3 +25,23 @@ func TestRegions_List(t *testing.T) { retryStatement(t, 3, testFunc) } + +func TestRegions_pgLimits(t *testing.T) { + client, teardown := createTestClient(t, "fixtures/TestRegions_pgLimits") + defer teardown() + + regions, err := client.ListRegions(context.Background(), nil) + require.NoError(t, err) + + // Filtering is not currently supported on capabilities + regionIdx := slices.IndexFunc(regions, func(region linodego.Region) bool { + return slices.Contains(region.Capabilities, "Placement Group") + }) + require.NotZero(t, regionIdx) + + region := regions[regionIdx] + + require.NotNil(t, region.PlacementGroupLimits) + require.NotZero(t, region.PlacementGroupLimits.MaximumLinodesPerPG) + require.NotZero(t, region.PlacementGroupLimits.MaximumPGsPerCustomer) +}