From ddabaec756996f461982a159186fe97d4dccd2c6 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 13 Jun 2024 13:51:34 -0400 Subject: [PATCH 1/7] WIP oops --- go.work.sum | 46 +- images.go | 105 +++-- .../fixtures/TestImage_CloudInit.yaml | 436 +++++------------- .../fixtures/TestImage_CreateUpload.yaml | 349 +------------- test/integration/images_test.go | 86 +++- 5 files changed, 324 insertions(+), 698 deletions(-) diff --git a/go.work.sum b/go.work.sum index 773bf0ee9..6c1d40342 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,8 +1,46 @@ +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/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +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/images.go b/images.go index 6ff8a3a89..a26ad2045 100644 --- a/images.go +++ b/images.go @@ -15,42 +15,64 @@ type ImageStatus string // ImageStatus options start with ImageStatus and include all Image statuses const ( - ImageStatusCreating ImageStatus = "creating" - ImageStatusPendingUpload ImageStatus = "pending_upload" - ImageStatusAvailable ImageStatus = "available" + ImageStatusAvailable ImageStatus = "available" + ImageStatusCreating ImageStatus = "creating" + ImageStatusPending ImageStatus = "pending" + ImageStatusPendingReplication ImageStatus = "pending replication" + ImageStatusPendingDeletion ImageStatus = "pending deletion" + ImageStatusPendingUpload ImageStatus = "pending_upload" + ImageStatusReplicating ImageStatus = "replicating" ) +// ImageRegion represents the status of an Image object in a given Region. +type ImageRegion struct { + Region string `json:"region"` + Status ImageStatus `json:"status"` +} + // Image represents a deployable Image object for use with Linode Instances type Image struct { - ID string `json:"id"` - CreatedBy string `json:"created_by"` - Capabilities []string `json:"capabilities"` - Label string `json:"label"` - Description string `json:"description"` - Type string `json:"type"` - Vendor string `json:"vendor"` - Status ImageStatus `json:"status"` - Size int `json:"size"` - IsPublic bool `json:"is_public"` - Deprecated bool `json:"deprecated"` - Updated *time.Time `json:"-"` - Created *time.Time `json:"-"` - Expiry *time.Time `json:"-"` - EOL *time.Time `json:"-"` + ID string `json:"id"` + CreatedBy string `json:"created_by"` + Capabilities []string `json:"capabilities"` + Label string `json:"label"` + Description string `json:"description"` + Type string `json:"type"` + Vendor string `json:"vendor"` + Status ImageStatus `json:"status"` + Size int `json:"size"` + TotalSize int `json:"total_size"` + IsPublic bool `json:"is_public"` + Deprecated bool `json:"deprecated"` + Regions []ImageRegion `json:"regions"` + Tags []string `json:"tags"` + + Updated *time.Time `json:"-"` + Created *time.Time `json:"-"` + Expiry *time.Time `json:"-"` + EOL *time.Time `json:"-"` } // ImageCreateOptions fields are those accepted by CreateImage type ImageCreateOptions struct { - DiskID int `json:"disk_id"` - Label string `json:"label"` - Description string `json:"description,omitempty"` - CloudInit bool `json:"cloud_init,omitempty"` + DiskID int `json:"disk_id"` + Label string `json:"label"` + Description string `json:"description,omitempty"` + CloudInit bool `json:"cloud_init,omitempty"` + Tags *[]string `json:"tags,omitempty"` } // ImageUpdateOptions fields are those accepted by UpdateImage type ImageUpdateOptions struct { - Label string `json:"label,omitempty"` - Description *string `json:"description,omitempty"` + Label string `json:"label,omitempty"` + Description *string `json:"description,omitempty"` + Tags *[]string `json:"tags,omitempty"` +} + +// ImageReplicateOptions represents the options accepted by the +// ReplicateImage(...) function. +type ImageReplicateOptions struct { + Regions []string `json:"regions"` } // ImageCreateUploadResponse fields are those returned by CreateImageUpload @@ -61,10 +83,11 @@ type ImageCreateUploadResponse struct { // ImageCreateUploadOptions fields are those accepted by CreateImageUpload type ImageCreateUploadOptions struct { - Region string `json:"region"` - Label string `json:"label"` - Description string `json:"description,omitempty"` - CloudInit bool `json:"cloud_init,omitempty"` + Region string `json:"region"` + Label string `json:"label"` + Description string `json:"description,omitempty"` + CloudInit bool `json:"cloud_init,omitempty"` + Tags *[]string `json:"tags,omitempty"` } // ImageUploadOptions fields are those accepted by UploadImage @@ -109,7 +132,7 @@ func (i Image) GetUpdateOptions() (iu ImageUpdateOptions) { return } -// ListImages lists Images +// ListImages lists Images. func (c *Client) ListImages(ctx context.Context, opts *ListOptions) ([]Image, error) { return getPaginatedResults[Image]( ctx, @@ -119,7 +142,7 @@ func (c *Client) ListImages(ctx context.Context, opts *ListOptions) ([]Image, er ) } -// GetImage gets the Image with the provided ID +// GetImage gets the Image with the provided ID. func (c *Client) GetImage(ctx context.Context, imageID string) (*Image, error) { return doGETRequest[Image]( ctx, @@ -128,7 +151,7 @@ func (c *Client) GetImage(ctx context.Context, imageID string) (*Image, error) { ) } -// CreateImage creates an Image +// CreateImage creates an Image. func (c *Client) CreateImage(ctx context.Context, opts ImageCreateOptions) (*Image, error) { return doPOSTRequest[Image]( ctx, @@ -138,7 +161,7 @@ func (c *Client) CreateImage(ctx context.Context, opts ImageCreateOptions) (*Ima ) } -// UpdateImage updates the Image with the specified id +// UpdateImage updates the Image with the specified id. func (c *Client) UpdateImage(ctx context.Context, imageID string, opts ImageUpdateOptions) (*Image, error) { return doPUTRequest[Image]( ctx, @@ -148,7 +171,17 @@ func (c *Client) UpdateImage(ctx context.Context, imageID string, opts ImageUpda ) } -// DeleteImage deletes the Image with the specified id +// ReplicateImage replicates an image to a given set of regions. +func (c *Client) ReplicateImage(ctx context.Context, imageID string, opts ImageReplicateOptions) (*Image, error) { + return doPOSTRequest[Image]( + ctx, + c, + formatAPIPath("images/%s/regions", imageID), + opts, + ) +} + +// DeleteImage deletes the Image with the specified id. func (c *Client) DeleteImage(ctx context.Context, imageID string) error { return doDELETERequest( ctx, @@ -157,7 +190,7 @@ func (c *Client) DeleteImage(ctx context.Context, imageID string) error { ) } -// CreateImageUpload creates an Image and an upload URL +// CreateImageUpload creates an Image and an upload URL. func (c *Client) CreateImageUpload(ctx context.Context, opts ImageCreateUploadOptions) (*Image, string, error) { result, err := doPOSTRequest[ImageCreateUploadResponse]( ctx, @@ -172,7 +205,7 @@ func (c *Client) CreateImageUpload(ctx context.Context, opts ImageCreateUploadOp return result.Image, result.UploadTo, nil } -// UploadImageToURL uploads the given image to the given upload URL +// UploadImageToURL uploads the given image to the given upload URL. func (c *Client) UploadImageToURL(ctx context.Context, uploadURL string, image io.Reader) error { // Linode-specific headers do not need to be sent to this endpoint req := resty.New().SetDebug(c.resty.Debug).R(). @@ -187,7 +220,7 @@ func (c *Client) UploadImageToURL(ctx context.Context, uploadURL string, image i return err } -// UploadImage creates and uploads an image +// UploadImage creates and uploads an image. func (c *Client) UploadImage(ctx context.Context, opts ImageUploadOptions) (*Image, error) { image, uploadURL, err := c.CreateImageUpload(ctx, ImageCreateUploadOptions{ Label: opts.Label, diff --git a/test/integration/fixtures/TestImage_CloudInit.yaml b/test/integration/fixtures/TestImage_CloudInit.yaml index 1c1598ff0..aa7e8792e 100644 --- a/test/integration/fixtures/TestImage_CloudInit.yaml +++ b/test/integration/fixtures/TestImage_CloudInit.yaml @@ -14,244 +14,73 @@ interactions: url: https://api.linode.com/v4beta/regions method: GET response: - body: '{"data": [{"id": "ap-west", "label": "Mumbai, IN", "country": "in", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "GPU Linodes", "Kubernetes", - "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", - "Metadata"], "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": "2400:8904::f03c:91ff:fea5:659, 2400:8904::f03c:91ff:fea5:9282, - 2400:8904::f03c:91ff:fea5:b9b3, 2400:8904::f03c:91ff:fea5:925a, 2400:8904::f03c:91ff:fea5:22cb, - 2400:8904::f03c:91ff:fea5:227a, 2400:8904::f03c:91ff:fea5:924c, 2400:8904::f03c:91ff:fea5:f7e2, - 2400:8904::f03c:91ff:fea5:2205, 2400:8904::f03c:91ff:fea5:9207"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "ca-central", "label": "Toronto, CA", "country": "ca", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud - Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata"], - "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": "2600:3c04::f03c:91ff:fea9:f63, 2600:3c04::f03c:91ff:fea9:f6d, - 2600:3c04::f03c:91ff:fea9:f80, 2600:3c04::f03c:91ff:fea9:f0f, 2600:3c04::f03c:91ff:fea9:f99, - 2600:3c04::f03c:91ff:fea9:fbd, 2600:3c04::f03c:91ff:fea9:fdd, 2600:3c04::f03c:91ff:fea9:fe2, - 2600:3c04::f03c:91ff:fea9:f68, 2600:3c04::f03c:91ff:fea9:f4a"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "ap-southeast", "label": "Sydney, AU", "country": "au", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud - Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata"], - "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": "2400:8907::f03c:92ff:fe6e:ec8, 2400:8907::f03c:92ff:fe6e:98e4, - 2400:8907::f03c:92ff:fe6e:1c58, 2400:8907::f03c:92ff:fe6e:c299, 2400:8907::f03c:92ff:fe6e:c210, - 2400:8907::f03c:92ff:fe6e:c219, 2400:8907::f03c:92ff:fe6e:1c5c, 2400:8907::f03c:92ff:fe6e:c24e, - 2400:8907::f03c:92ff:fe6e:e6b, 2400:8907::f03c:92ff:fe6e:e3d"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "us-iad", "label": "Washington, DC", "country": "us", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", - "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium - Plans"], "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": "2600:3c05::f03c:93ff:feb6:43b6, - 2600:3c05::f03c:93ff:feb6:4365, 2600:3c05::f03c:93ff:feb6:43c2, 2600:3c05::f03c:93ff:feb6:e441, - 2600:3c05::f03c:93ff:feb6:94ef, 2600:3c05::f03c:93ff:feb6:94ba, 2600:3c05::f03c:93ff:feb6:94a8, - 2600:3c05::f03c:93ff:feb6:9413, 2600:3c05::f03c:93ff:feb6:9443, 2600:3c05::f03c:93ff:feb6:94e0"}, + 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": "us-ord", "label": "Chicago, IL", "country": - "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "VPCs", - "Managed Databases", "Metadata", "Premium Plans"], "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": - "2600:3c06::f03c:93ff:fed0:e5fc, 2600:3c06::f03c:93ff:fed0:e54b, 2600:3c06::f03c:93ff:fed0:e572, - 2600:3c06::f03c:93ff:fed0:e530, 2600:3c06::f03c:93ff:fed0:e597, 2600:3c06::f03c:93ff:fed0:e511, - 2600:3c06::f03c:93ff:fed0:e5f2, 2600:3c06::f03c:93ff:fed0:e5bf, 2600:3c06::f03c:93ff:fed0:e529, - 2600:3c06::f03c:93ff:fed0:e5a3"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "fr-par", "label": - "Paris, FR", "country": "fr", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", - "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans"], "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": "2600:3c07::f03c:93ff:fef2:2e63, 2600:3c07::f03c:93ff:fef2:2ec7, 2600:3c07::f03c:93ff:fef2:0dee, - 2600:3c07::f03c:93ff:fef2:0d25, 2600:3c07::f03c:93ff:fef2:0de0, 2600:3c07::f03c:93ff:fef2:2e29, - 2600:3c07::f03c:93ff:fef2:0dda, 2600:3c07::f03c:93ff:fef2:0d82, 2600:3c07::f03c:93ff:fef2:b3ac, - 2600:3c07::f03c:93ff:fef2:b3a8"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "us-sea", "label": - "Seattle, WA", "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", - "Vlans", "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c0a::f03c:93ff:fe54:c6da, 2600:3c0a::f03c:93ff:fe54:c691, 2600:3c0a::f03c:93ff:fe54:c68d, - 2600:3c0a::f03c:93ff:fe54:c61e, 2600:3c0a::f03c:93ff:fe54:c653, 2600:3c0a::f03c:93ff:fe54:c64c, - 2600:3c0a::f03c:93ff:fe54:c68a, 2600:3c0a::f03c:93ff:fe54:c697, 2600:3c0a::f03c:93ff:fe54:c60f, - 2600:3c0a::f03c:93ff:fe54:c6a0"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "br-gru", "label": - "Sao Paulo, BR", "country": "br", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c0d::f03c:93ff:fe3d:51cb, - 2600:3c0d::f03c:93ff:fe3d:51a7, 2600:3c0d::f03c:93ff:fe3d:51a9, 2600:3c0d::f03c:93ff:fe3d:5119, - 2600:3c0d::f03c:93ff:fe3d:51fe, 2600:3c0d::f03c:93ff:fe3d:517c, 2600:3c0d::f03c:93ff:fe3d:5144, - 2600:3c0d::f03c:93ff:fe3d:5170, 2600:3c0d::f03c:93ff:fe3d:51cc, 2600:3c0d::f03c:93ff:fe3d:516c"}, + 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": "nl-ams", "label": "Amsterdam, NL", "country": - "nl", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", "VPCs", "Metadata", - "Premium Plans"], "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": "2600:3c0e::f03c:93ff:fe9d:2d10, 2600:3c0e::f03c:93ff:fe9d:2d89, - 2600:3c0e::f03c:93ff:fe9d:2d79, 2600:3c0e::f03c:93ff:fe9d:2d96, 2600:3c0e::f03c:93ff:fe9d:2da5, - 2600:3c0e::f03c:93ff:fe9d:2d34, 2600:3c0e::f03c:93ff:fe9d:2d68, 2600:3c0e::f03c:93ff:fe9d:2d17, - 2600:3c0e::f03c:93ff:fe9d:2d45, 2600:3c0e::f03c:93ff:fe9d:2d5c"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "se-sto", "label": "Stockholm, SE", "country": "se", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", - "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c09::f03c:93ff:fea9:4dbe, 2600:3c09::f03c:93ff:fea9:4d63, - 2600:3c09::f03c:93ff:fea9:4dce, 2600:3c09::f03c:93ff:fea9:4dbb, 2600:3c09::f03c:93ff:fea9:4d99, - 2600:3c09::f03c:93ff:fea9:4d26, 2600:3c09::f03c:93ff:fea9:4de0, 2600:3c09::f03c:93ff:fea9:4d69, - 2600:3c09::f03c:93ff:fea9:4dbf, 2600:3c09::f03c:93ff:fea9:4da6"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "es-mad", "label": "Madrid, ES", "country": "es", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", - "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans"], "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": "2a01:7e02::f03c:93ff:feea:b585, 2a01:7e02::f03c:93ff:feea:b5ab, - 2a01:7e02::f03c:93ff:feea:b5c6, 2a01:7e02::f03c:93ff:feea:b592, 2a01:7e02::f03c:93ff:feea:b5aa, - 2a01:7e02::f03c:93ff:feea:b5d3, 2a01:7e02::f03c:93ff:feea:b5d7, 2a01:7e02::f03c:93ff:feea:b528, - 2a01:7e02::f03c:93ff:feea:b522, 2a01:7e02::f03c:93ff:feea:b51a"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "in-maa", "label": "Chennai, IN", "country": "in", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", - "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c08::f03c:93ff:fe7c:1135, 2600:3c08::f03c:93ff:fe7c:11f8, 2600:3c08::f03c:93ff:fe7c:11d2, - 2600:3c08::f03c:93ff:fe7c:11a7, 2600:3c08::f03c:93ff:fe7c:11ad, 2600:3c08::f03c:93ff:fe7c:110a, - 2600:3c08::f03c:93ff:fe7c:11f9, 2600:3c08::f03c:93ff:fe7c:1137, 2600:3c08::f03c:93ff:fe7c:11db, - 2600:3c08::f03c:93ff:fe7c:1164"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "jp-osa", "label": - "Osaka, JP", "country": "jp", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", - "Vlans", "VPCs", "Metadata", "Premium Plans"], "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": "2400:8905::f03c:93ff:fe9d:b085, 2400:8905::f03c:93ff:fe9d:b012, 2400:8905::f03c:93ff:fe9d:b09b, - 2400:8905::f03c:93ff:fe9d:b0d8, 2400:8905::f03c:93ff:fe9d:259f, 2400:8905::f03c:93ff:fe9d:b006, - 2400:8905::f03c:93ff:fe9d:b084, 2400:8905::f03c:93ff:fe9d:b0ce, 2400:8905::f03c:93ff:fe9d:25ea, - 2400:8905::f03c:93ff:fe9d:b086"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "it-mil", "label": - "Milan, IT", "country": "it", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c0b::f03c:93ff:feba:d513, 2600:3c0b::f03c:93ff:feba:d5c3, 2600:3c0b::f03c:93ff:feba:d597, - 2600:3c0b::f03c:93ff:feba:d5fb, 2600:3c0b::f03c:93ff:feba:d51f, 2600:3c0b::f03c:93ff:feba:d58e, - 2600:3c0b::f03c:93ff:feba:d5d5, 2600:3c0b::f03c:93ff:feba:d534, 2600:3c0b::f03c:93ff:feba:d57c, - 2600:3c0b::f03c:93ff:feba:d529"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "us-mia", "label": - "Miami, FL", "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans"], "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": "2a01:7e04::f03c:93ff:fead:d31f, 2a01:7e04::f03c:93ff:fead:d37f, 2a01:7e04::f03c:93ff:fead:d30c, - 2a01:7e04::f03c:93ff:fead:d318, 2a01:7e04::f03c:93ff:fead:d316, 2a01:7e04::f03c:93ff:fead:d339, - 2a01:7e04::f03c:93ff:fead:d367, 2a01:7e04::f03c:93ff:fead:d395, 2a01:7e04::f03c:93ff:fead:d3d0, - 2a01:7e04::f03c:93ff:fead:d38e"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "id-cgk", "label": - "Jakarta, ID", "country": "id", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c0c::f03c:93ff:feed:a90b, 2600:3c0c::f03c:93ff:feed:a9a5, 2600:3c0c::f03c:93ff:feed:a935, - 2600:3c0c::f03c:93ff:feed:a930, 2600:3c0c::f03c:93ff:feed:a95c, 2600:3c0c::f03c:93ff:feed:a9ad, - 2600:3c0c::f03c:93ff:feed:a9f2, 2600:3c0c::f03c:93ff:feed:a9ff, 2600:3c0c::f03c:93ff:feed:a9c8, - 2600:3c0c::f03c:93ff:feed:a96b"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "us-lax", "label": - "Los Angeles, CA", "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans"], "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": "2a01:7e03::f03c:93ff:feb1:b789, 2a01:7e03::f03c:93ff:feb1:b717, 2a01:7e03::f03c:93ff:feb1:b707, - 2a01:7e03::f03c:93ff:feb1:b7ab, 2a01:7e03::f03c:93ff:feb1:b7e2, 2a01:7e03::f03c:93ff:feb1:b709, - 2a01:7e03::f03c:93ff:feb1:b7a6, 2a01:7e03::f03c:93ff:feb1:b750, 2a01:7e03::f03c:93ff:feb1:b76e, - 2a01:7e03::f03c:93ff:feb1:b7a2"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "us-central", - "label": "Dallas, TX", "country": "us", "capabilities": ["Linodes", "Backups", - "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", "Vlans", "Block - Storage Migrations", "Managed Databases", "Metadata"], "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": - "2600:3c00::2, 2600:3c00::9, 2600:3c00::7, 2600:3c00::5, 2600:3c00::3, 2600:3c00::8, - 2600:3c00::6, 2600:3c00::4, 2600:3c00::c, 2600:3c00::b"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "us-west", "label": "Fremont, CA", "country": "us", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud - Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata"], - "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": "2600:3c01::2, 2600:3c01::9, 2600:3c01::5, 2600:3c01::7, - 2600:3c01::3, 2600:3c01::8, 2600:3c01::4, 2600:3c01::b, 2600:3c01::c, 2600:3c01::6"}, + 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", "country": - "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block - Storage Migrations", "Managed Databases", "Metadata"], "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": "2600:3c02::3, - 2600:3c02::5, 2600:3c02::4, 2600:3c02::6, 2600:3c02::c, 2600:3c02::7, 2600:3c02::2, - 2600:3c02::9, 2600:3c02::8, 2600:3c02::b"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "us-east", "label": - "Newark, NJ", "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", + 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", - "Vlans", "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": "2600:3c03::7, 2600:3c03::4, 2600:3c03::9, 2600:3c03::6, 2600:3c03::3, - 2600:3c03::c, 2600:3c03::5, 2600:3c03::b, 2600:3c03::2, 2600:3c03::8"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "eu-west", "label": "London, UK", "country": "gb", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud - Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata"], - "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": "2a01:7e00::9, 2a01:7e00::3, 2a01:7e00::c, 2a01:7e00::5, - 2a01:7e00::6, 2a01:7e00::8, 2a01:7e00::b, 2a01:7e00::4, 2a01:7e00::7, 2a01:7e00::2"}, + "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", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block - Storage Migrations", "Managed Databases", "Metadata"], "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": - "2400:8901::5, 2400:8901::4, 2400:8901::b, 2400:8901::3, 2400:8901::9, 2400:8901::2, - 2400:8901::8, 2400:8901::7, 2400:8901::c, 2400:8901::6"}, "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", "Block Storage", "Object Storage", "GPU - Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", - "Managed Databases", "Metadata"], "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": "2a01:7e01::5, 2a01:7e01::9, - 2a01:7e01::7, 2a01:7e01::c, 2a01:7e01::2, 2a01:7e01::4, 2a01:7e01::3, 2a01:7e01::6, - 2a01:7e01::b, 2a01:7e01::8"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "ap-northeast", - "label": "Tokyo, JP", "country": "jp", "capabilities": ["Linodes", "Backups", - "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", "Vlans", "Block - Storage Migrations", "Managed Databases", "Metadata"], "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": - "2400:8902::3, 2400:8902::6, 2400:8902::c, 2400:8902::4, 2400:8902::2, 2400:8902::8, - 2400:8902::7, 2400:8902::5, 2400:8902::b, 2400:8902::9"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}], "page": 1, "pages": 1, "results": 25}' + "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" @@ -264,23 +93,20 @@ interactions: Access-Control-Expose-Headers: - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status Cache-Control: - - max-age=0, no-cache, no-store - Connection: - - keep-alive + - private, max-age=900 + - private, max-age=60, s-maxage=60 Content-Security-Policy: - default-src 'none' Content-Type: - application/json - Expires: - - Fri, 31 May 2024 17:09:40 GMT - Pragma: - - no-cache + Server: + - nginx Strict-Transport-Security: - max-age=31536000 Vary: + - Accept-Encoding - Authorization, X-Filter - Authorization, X-Filter - - Accept-Encoding X-Accepted-Oauth-Scopes: - '*' X-Content-Type-Options: @@ -289,10 +115,7 @@ interactions: - DENY - DENY X-Oauth-Scopes: - - account:read_write databases:read_write domains:read_write events:read_write - firewall:read_write images:read_write ips:read_write linodes:read_write lke:read_write - longview:read_write nodebalancers:read_write object_storage:read_write stackscripts:read_write - volumes:read_write vpc:read_write + - '*' X-Ratelimit-Limit: - "400" X-Xss-Protection: @@ -301,7 +124,7 @@ interactions: code: 200 duration: "" - request: - body: '{"region":"ap-west","type":"g6-nanode-1","label":"go-test-ins-313l0iwln6c9","root_pass":"FLxlu(0=0R$ALhX)'';n83t7AJ/s65AnsJ2}|6v8ss*3QdP)5^\\V99Ej1V\\k2|\u0026Am","image":"linode/debian9","firewall_id":501338,"booted":false}' + body: '{"region":"us-east","type":"g6-nanode-1","label":"go-test-ins-49q41ch6nri4","root_pass":"5)buQTe6(wiB43\u003c4-21o8`a-N*\u003c:X[71CnL\u0026jA)Qq8bIn\u003c:3cxJcH@UIQ''1K2d55","image":"linode/debian9","firewall_id":34033,"booted":false}' form: {} headers: Accept: @@ -313,16 +136,16 @@ interactions: url: https://api.linode.com/v4beta/linode/instances method: POST response: - body: '{"id": 59541503, "label": "go-test-ins-313l0iwln6c9", "group": "", "status": + body: '{"id": 25189725, "label": "go-test-ins-49q41ch6nri4", "group": "", "status": "provisioning", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", - "type": "g6-nanode-1", "ipv4": ["45.79.125.26"], "ipv6": "2400:8904::f03c:94ff:fe49:7305/128", - "image": "linode/debian9", "region": "ap-west", "specs": {"disk": 25600, "memory": + "type": "g6-nanode-1", "ipv4": ["97.107.143.188"], "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": - true, "available": false, "schedule": {"day": null, "window": null}, "last_successful": + false, "available": false, "schedule": {"day": null, "window": null}, "last_successful": null}, "hypervisor": "kvm", "watchdog_enabled": true, "tags": [], "host_uuid": - "d80b575c03e15cd02b19144819366fbb2a318648", "has_user_data": false, "placement_group": - null}' + "53257e56e7692e143036a5fbf7daaf92b5350bf3", "has_user_data": false, "placement_group": + null, "disk_encryption": "enabled", "lke_cluster_id": null}' headers: Access-Control-Allow-Credentials: - "true" @@ -335,22 +158,17 @@ interactions: Access-Control-Expose-Headers: - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status Cache-Control: - - max-age=0, no-cache, no-store - Connection: - - keep-alive - Content-Length: - - "764" + - private, max-age=60, s-maxage=60 Content-Security-Policy: - default-src 'none' Content-Type: - application/json - Expires: - - Fri, 31 May 2024 17:09:41 GMT - Pragma: - - no-cache + Server: + - nginx Strict-Transport-Security: - max-age=31536000 Vary: + - Accept-Encoding - Authorization, X-Filter X-Accepted-Oauth-Scopes: - linodes:read_write @@ -360,10 +178,7 @@ interactions: - DENY - DENY X-Oauth-Scopes: - - account:read_write databases:read_write domains:read_write events:read_write - firewall:read_write images:read_write ips:read_write linodes:read_write lke:read_write - longview:read_write nodebalancers:read_write object_storage:read_write stackscripts:read_write - volumes:read_write vpc:read_write + - '*' X-Ratelimit-Limit: - "10" X-Xss-Protection: @@ -381,14 +196,15 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/linode/instances/59541503/disks + url: https://api.linode.com/v4beta/linode/instances/25189725/disks method: GET response: - body: '{"data": [{"id": 117259526, "status": "not ready", "label": "Debian 9 Disk", + body: '{"data": [{"id": 6268771, "status": "not ready", "label": "Debian 9 Disk", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "filesystem": - "ext4", "size": 25088}, {"id": 117259527, "status": "not ready", "label": "512 - MB Swap Image", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", - "filesystem": "swap", "size": 512}], "page": 1, "pages": 1, "results": 2}' + "ext4", "size": 25088, "disk_encryption": "enabled"}, {"id": 6268772, "status": + "not ready", "label": "512 MB Swap Image", "created": "2018-01-02T03:04:05", + "updated": "2018-01-02T03:04:05", "filesystem": "swap", "size": 512, "disk_encryption": + "enabled"}], "page": 1, "pages": 1, "results": 2}' headers: Access-Control-Allow-Credentials: - "true" @@ -401,22 +217,18 @@ interactions: Access-Control-Expose-Headers: - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status Cache-Control: - - max-age=0, no-cache, no-store - Connection: - - keep-alive - Content-Length: - - "395" + - 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 - Expires: - - Fri, 31 May 2024 17:09:41 GMT - Pragma: - - no-cache + Server: + - nginx Strict-Transport-Security: - max-age=31536000 Vary: + - Accept-Encoding - Authorization, X-Filter - Authorization, X-Filter X-Accepted-Oauth-Scopes: @@ -427,10 +239,7 @@ interactions: - DENY - DENY X-Oauth-Scopes: - - account:read_write databases:read_write domains:read_write events:read_write - firewall:read_write images:read_write ips:read_write linodes:read_write lke:read_write - longview:read_write nodebalancers:read_write object_storage:read_write stackscripts:read_write - volumes:read_write vpc:read_write + - '*' X-Ratelimit-Limit: - "400" X-Xss-Protection: @@ -439,7 +248,7 @@ interactions: code: 200 duration: "" - request: - body: '{"disk_id":117259526,"label":"linodego-test-cloud-init","cloud_init":true}' + body: '{"disk_id":6268771,"label":"linodego-test-cloud-init","cloud_init":true,"tags":["test1","test2"]}' form: {} headers: Accept: @@ -451,11 +260,12 @@ interactions: url: https://api.linode.com/v4beta/images method: POST response: - body: '{"id": "private/25476416", "label": "linodego-test-cloud-init", "description": + body: '{"id": "private/10044171", "label": "linodego-test-cloud-init", "description": "", "created": "2018-01-02T03:04:05", "updated": "2018-01-02T03:04:05", "size": - 25088, "created_by": "youjungk01", "type": "manual", "tags": [], "is_public": - false, "deprecated": false, "vendor": null, "expiry": null, "eol": null, "status": - "creating", "capabilities": ["cloud-init"]}' + 25088, "created_by": "lgarber", "type": "manual", "tags": ["test1", "test2"], + "is_public": false, "deprecated": false, "vendor": null, "expiry": null, "eol": + null, "status": "creating", "capabilities": ["cloud-init"], "regions": [], "total_size": + 25088}' headers: Access-Control-Allow-Credentials: - "true" @@ -468,22 +278,17 @@ interactions: Access-Control-Expose-Headers: - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status Cache-Control: - - max-age=0, no-cache, no-store - Connection: - - keep-alive - Content-Length: - - "363" + - private, max-age=60, s-maxage=60 Content-Security-Policy: - default-src 'none' Content-Type: - application/json - Expires: - - Fri, 31 May 2024 17:09:41 GMT - Pragma: - - no-cache + Server: + - nginx Strict-Transport-Security: - max-age=31536000 Vary: + - Accept-Encoding - Authorization, X-Filter X-Accepted-Oauth-Scopes: - images:read_write,linodes:read_only @@ -493,10 +298,7 @@ interactions: - DENY - DENY X-Oauth-Scopes: - - account:read_write databases:read_write domains:read_write events:read_write - firewall:read_write images:read_write ips:read_write linodes:read_write lke:read_write - longview:read_write nodebalancers:read_write object_storage:read_write stackscripts:read_write - volumes:read_write vpc:read_write + - '*' X-Ratelimit-Limit: - "400" X-Xss-Protection: @@ -514,7 +316,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/images/private%2F25476416 + url: https://api.linode.com/v4beta/images/private%2F10044171 method: DELETE response: body: '{}' @@ -530,19 +332,15 @@ interactions: Access-Control-Expose-Headers: - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status Cache-Control: - - max-age=0, no-cache, no-store - Connection: - - keep-alive + - private, max-age=60, s-maxage=60 Content-Length: - "2" Content-Security-Policy: - default-src 'none' Content-Type: - application/json - Expires: - - Fri, 31 May 2024 17:09:42 GMT - Pragma: - - no-cache + Server: + - nginx Strict-Transport-Security: - max-age=31536000 Vary: @@ -555,10 +353,7 @@ interactions: - DENY - DENY X-Oauth-Scopes: - - account:read_write databases:read_write domains:read_write events:read_write - firewall:read_write images:read_write ips:read_write linodes:read_write lke:read_write - longview:read_write nodebalancers:read_write object_storage:read_write stackscripts:read_write - volumes:read_write vpc:read_write + - '*' X-Ratelimit-Limit: - "400" X-Xss-Protection: @@ -576,7 +371,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/linode/instances/59541503 + url: https://api.linode.com/v4beta/linode/instances/25189725 method: DELETE response: body: '{}' @@ -592,19 +387,15 @@ interactions: Access-Control-Expose-Headers: - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status Cache-Control: - - max-age=0, no-cache, no-store - Connection: - - keep-alive + - private, max-age=60, s-maxage=60 Content-Length: - "2" Content-Security-Policy: - default-src 'none' Content-Type: - application/json - Expires: - - Fri, 31 May 2024 17:09:42 GMT - Pragma: - - no-cache + Server: + - nginx Strict-Transport-Security: - max-age=31536000 Vary: @@ -617,10 +408,7 @@ interactions: - DENY - DENY X-Oauth-Scopes: - - account:read_write databases:read_write domains:read_write events:read_write - firewall:read_write images:read_write ips:read_write linodes:read_write lke:read_write - longview:read_write nodebalancers:read_write object_storage:read_write stackscripts:read_write - volumes:read_write vpc:read_write + - '*' X-Ratelimit-Limit: - "400" X-Xss-Protection: diff --git a/test/integration/fixtures/TestImage_CreateUpload.yaml b/test/integration/fixtures/TestImage_CreateUpload.yaml index 1ef52ba67..7fd198e20 100644 --- a/test/integration/fixtures/TestImage_CreateUpload.yaml +++ b/test/integration/fixtures/TestImage_CreateUpload.yaml @@ -2,307 +2,8 @@ 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, IN", "country": "in", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "GPU Linodes", "Kubernetes", - "Cloud Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", - "Metadata"], "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": "2400:8904::f03c:91ff:fea5:659, 2400:8904::f03c:91ff:fea5:9282, - 2400:8904::f03c:91ff:fea5:b9b3, 2400:8904::f03c:91ff:fea5:925a, 2400:8904::f03c:91ff:fea5:22cb, - 2400:8904::f03c:91ff:fea5:227a, 2400:8904::f03c:91ff:fea5:924c, 2400:8904::f03c:91ff:fea5:f7e2, - 2400:8904::f03c:91ff:fea5:2205, 2400:8904::f03c:91ff:fea5:9207"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "ca-central", "label": "Toronto, CA", "country": "ca", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud - Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata"], - "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": "2600:3c04::f03c:91ff:fea9:f63, 2600:3c04::f03c:91ff:fea9:f6d, - 2600:3c04::f03c:91ff:fea9:f80, 2600:3c04::f03c:91ff:fea9:f0f, 2600:3c04::f03c:91ff:fea9:f99, - 2600:3c04::f03c:91ff:fea9:fbd, 2600:3c04::f03c:91ff:fea9:fdd, 2600:3c04::f03c:91ff:fea9:fe2, - 2600:3c04::f03c:91ff:fea9:f68, 2600:3c04::f03c:91ff:fea9:f4a"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "ap-southeast", "label": "Sydney, AU", "country": "au", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud - Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata"], - "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": "2400:8907::f03c:92ff:fe6e:ec8, 2400:8907::f03c:92ff:fe6e:98e4, - 2400:8907::f03c:92ff:fe6e:1c58, 2400:8907::f03c:92ff:fe6e:c299, 2400:8907::f03c:92ff:fe6e:c210, - 2400:8907::f03c:92ff:fe6e:c219, 2400:8907::f03c:92ff:fe6e:1c5c, 2400:8907::f03c:92ff:fe6e:c24e, - 2400:8907::f03c:92ff:fe6e:e6b, 2400:8907::f03c:92ff:fe6e:e3d"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "us-iad", "label": "Washington, DC", "country": "us", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", - "Cloud Firewall", "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium - Plans"], "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": "2600:3c05::f03c:93ff:feb6:43b6, - 2600:3c05::f03c:93ff:feb6:4365, 2600:3c05::f03c:93ff:feb6:43c2, 2600:3c05::f03c:93ff:feb6:e441, - 2600:3c05::f03c:93ff:feb6:94ef, 2600:3c05::f03c:93ff:feb6:94ba, 2600:3c05::f03c:93ff:feb6:94a8, - 2600:3c05::f03c:93ff:feb6:9413, 2600:3c05::f03c:93ff:feb6:9443, 2600:3c05::f03c:93ff:feb6:94e0"}, - "placement_group_limits": {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": - 5}, "site_type": "core"}, {"id": "us-ord", "label": "Chicago, IL", "country": - "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "VPCs", - "Managed Databases", "Metadata", "Premium Plans"], "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": - "2600:3c06::f03c:93ff:fed0:e5fc, 2600:3c06::f03c:93ff:fed0:e54b, 2600:3c06::f03c:93ff:fed0:e572, - 2600:3c06::f03c:93ff:fed0:e530, 2600:3c06::f03c:93ff:fed0:e597, 2600:3c06::f03c:93ff:fed0:e511, - 2600:3c06::f03c:93ff:fed0:e5f2, 2600:3c06::f03c:93ff:fed0:e5bf, 2600:3c06::f03c:93ff:fed0:e529, - 2600:3c06::f03c:93ff:fed0:e5a3"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "fr-par", "label": - "Paris, FR", "country": "fr", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", - "Vlans", "VPCs", "Managed Databases", "Metadata", "Premium Plans"], "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": "2600:3c07::f03c:93ff:fef2:2e63, 2600:3c07::f03c:93ff:fef2:2ec7, 2600:3c07::f03c:93ff:fef2:0dee, - 2600:3c07::f03c:93ff:fef2:0d25, 2600:3c07::f03c:93ff:fef2:0de0, 2600:3c07::f03c:93ff:fef2:2e29, - 2600:3c07::f03c:93ff:fef2:0dda, 2600:3c07::f03c:93ff:fef2:0d82, 2600:3c07::f03c:93ff:fef2:b3ac, - 2600:3c07::f03c:93ff:fef2:b3a8"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "us-sea", "label": - "Seattle, WA", "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", - "Vlans", "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c0a::f03c:93ff:fe54:c6da, 2600:3c0a::f03c:93ff:fe54:c691, 2600:3c0a::f03c:93ff:fe54:c68d, - 2600:3c0a::f03c:93ff:fe54:c61e, 2600:3c0a::f03c:93ff:fe54:c653, 2600:3c0a::f03c:93ff:fe54:c64c, - 2600:3c0a::f03c:93ff:fe54:c68a, 2600:3c0a::f03c:93ff:fe54:c697, 2600:3c0a::f03c:93ff:fe54:c60f, - 2600:3c0a::f03c:93ff:fe54:c6a0"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "br-gru", "label": - "Sao Paulo, BR", "country": "br", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c0d::f03c:93ff:fe3d:51cb, - 2600:3c0d::f03c:93ff:fe3d:51a7, 2600:3c0d::f03c:93ff:fe3d:51a9, 2600:3c0d::f03c:93ff:fe3d:5119, - 2600:3c0d::f03c:93ff:fe3d:51fe, 2600:3c0d::f03c:93ff:fe3d:517c, 2600:3c0d::f03c:93ff:fe3d:5144, - 2600:3c0d::f03c:93ff:fe3d:5170, 2600:3c0d::f03c:93ff:fe3d:51cc, 2600:3c0d::f03c:93ff:fe3d:516c"}, - "placement_group_limits": {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": - 5}, "site_type": "core"}, {"id": "nl-ams", "label": "Amsterdam, NL", "country": - "nl", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", "VPCs", "Metadata", - "Premium Plans"], "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": "2600:3c0e::f03c:93ff:fe9d:2d10, 2600:3c0e::f03c:93ff:fe9d:2d89, - 2600:3c0e::f03c:93ff:fe9d:2d79, 2600:3c0e::f03c:93ff:fe9d:2d96, 2600:3c0e::f03c:93ff:fe9d:2da5, - 2600:3c0e::f03c:93ff:fe9d:2d34, 2600:3c0e::f03c:93ff:fe9d:2d68, 2600:3c0e::f03c:93ff:fe9d:2d17, - 2600:3c0e::f03c:93ff:fe9d:2d45, 2600:3c0e::f03c:93ff:fe9d:2d5c"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "se-sto", "label": "Stockholm, SE", "country": "se", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", - "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c09::f03c:93ff:fea9:4dbe, 2600:3c09::f03c:93ff:fea9:4d63, - 2600:3c09::f03c:93ff:fea9:4dce, 2600:3c09::f03c:93ff:fea9:4dbb, 2600:3c09::f03c:93ff:fea9:4d99, - 2600:3c09::f03c:93ff:fea9:4d26, 2600:3c09::f03c:93ff:fea9:4de0, 2600:3c09::f03c:93ff:fea9:4d69, - 2600:3c09::f03c:93ff:fea9:4dbf, 2600:3c09::f03c:93ff:fea9:4da6"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "es-mad", "label": "Madrid, ES", "country": "es", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", - "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans"], "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": "2a01:7e02::f03c:93ff:feea:b585, 2a01:7e02::f03c:93ff:feea:b5ab, - 2a01:7e02::f03c:93ff:feea:b5c6, 2a01:7e02::f03c:93ff:feea:b592, 2a01:7e02::f03c:93ff:feea:b5aa, - 2a01:7e02::f03c:93ff:feea:b5d3, 2a01:7e02::f03c:93ff:feea:b5d7, 2a01:7e02::f03c:93ff:feea:b528, - 2a01:7e02::f03c:93ff:feea:b522, 2a01:7e02::f03c:93ff:feea:b51a"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "in-maa", "label": "Chennai, IN", "country": "in", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Object Storage", "Kubernetes", - "Cloud Firewall", "Vlans", "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c08::f03c:93ff:fe7c:1135, 2600:3c08::f03c:93ff:fe7c:11f8, 2600:3c08::f03c:93ff:fe7c:11d2, - 2600:3c08::f03c:93ff:fe7c:11a7, 2600:3c08::f03c:93ff:fe7c:11ad, 2600:3c08::f03c:93ff:fe7c:110a, - 2600:3c08::f03c:93ff:fe7c:11f9, 2600:3c08::f03c:93ff:fe7c:1137, 2600:3c08::f03c:93ff:fe7c:11db, - 2600:3c08::f03c:93ff:fe7c:1164"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "jp-osa", "label": - "Osaka, JP", "country": "jp", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", - "Vlans", "VPCs", "Metadata", "Premium Plans"], "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": "2400:8905::f03c:93ff:fe9d:b085, 2400:8905::f03c:93ff:fe9d:b012, 2400:8905::f03c:93ff:fe9d:b09b, - 2400:8905::f03c:93ff:fe9d:b0d8, 2400:8905::f03c:93ff:fe9d:259f, 2400:8905::f03c:93ff:fe9d:b006, - 2400:8905::f03c:93ff:fe9d:b084, 2400:8905::f03c:93ff:fe9d:b0ce, 2400:8905::f03c:93ff:fe9d:25ea, - 2400:8905::f03c:93ff:fe9d:b086"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "it-mil", "label": - "Milan, IT", "country": "it", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c0b::f03c:93ff:feba:d513, 2600:3c0b::f03c:93ff:feba:d5c3, 2600:3c0b::f03c:93ff:feba:d597, - 2600:3c0b::f03c:93ff:feba:d5fb, 2600:3c0b::f03c:93ff:feba:d51f, 2600:3c0b::f03c:93ff:feba:d58e, - 2600:3c0b::f03c:93ff:feba:d5d5, 2600:3c0b::f03c:93ff:feba:d534, 2600:3c0b::f03c:93ff:feba:d57c, - 2600:3c0b::f03c:93ff:feba:d529"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "us-mia", "label": - "Miami, FL", "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans"], "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": "2a01:7e04::f03c:93ff:fead:d31f, 2a01:7e04::f03c:93ff:fead:d37f, 2a01:7e04::f03c:93ff:fead:d30c, - 2a01:7e04::f03c:93ff:fead:d318, 2a01:7e04::f03c:93ff:fead:d316, 2a01:7e04::f03c:93ff:fead:d339, - 2a01:7e04::f03c:93ff:fead:d367, 2a01:7e04::f03c:93ff:fead:d395, 2a01:7e04::f03c:93ff:fead:d3d0, - 2a01:7e04::f03c:93ff:fead:d38e"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "id-cgk", "label": - "Jakarta, ID", "country": "id", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans"], "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": "2600:3c0c::f03c:93ff:feed:a90b, 2600:3c0c::f03c:93ff:feed:a9a5, 2600:3c0c::f03c:93ff:feed:a935, - 2600:3c0c::f03c:93ff:feed:a930, 2600:3c0c::f03c:93ff:feed:a95c, 2600:3c0c::f03c:93ff:feed:a9ad, - 2600:3c0c::f03c:93ff:feed:a9f2, 2600:3c0c::f03c:93ff:feed:a9ff, 2600:3c0c::f03c:93ff:feed:a9c8, - 2600:3c0c::f03c:93ff:feed:a96b"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "us-lax", "label": - "Los Angeles, CA", "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "Kubernetes", "Cloud Firewall", "Vlans", - "VPCs", "Metadata", "Premium Plans"], "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": "2a01:7e03::f03c:93ff:feb1:b789, 2a01:7e03::f03c:93ff:feb1:b717, 2a01:7e03::f03c:93ff:feb1:b707, - 2a01:7e03::f03c:93ff:feb1:b7ab, 2a01:7e03::f03c:93ff:feb1:b7e2, 2a01:7e03::f03c:93ff:feb1:b709, - 2a01:7e03::f03c:93ff:feb1:b7a6, 2a01:7e03::f03c:93ff:feb1:b750, 2a01:7e03::f03c:93ff:feb1:b76e, - 2a01:7e03::f03c:93ff:feb1:b7a2"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "us-central", - "label": "Dallas, TX", "country": "us", "capabilities": ["Linodes", "Backups", - "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", "Vlans", "Block - Storage Migrations", "Managed Databases", "Metadata"], "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": - "2600:3c00::2, 2600:3c00::9, 2600:3c00::7, 2600:3c00::5, 2600:3c00::3, 2600:3c00::8, - 2600:3c00::6, 2600:3c00::4, 2600:3c00::c, 2600:3c00::b"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "us-west", "label": "Fremont, CA", "country": "us", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud - Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata"], - "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": "2600:3c01::2, 2600:3c01::9, 2600:3c01::5, 2600:3c01::7, - 2600:3c01::3, 2600:3c01::8, 2600:3c01::4, 2600:3c01::b, 2600:3c01::c, 2600:3c01::6"}, - "placement_group_limits": {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": - 5}, "site_type": "core"}, {"id": "us-southeast", "label": "Atlanta, GA", "country": - "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block - Storage Migrations", "Managed Databases", "Metadata"], "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": "2600:3c02::3, - 2600:3c02::5, 2600:3c02::4, 2600:3c02::6, 2600:3c02::c, 2600:3c02::7, 2600:3c02::2, - 2600:3c02::9, 2600:3c02::8, 2600:3c02::b"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "us-east", "label": - "Newark, NJ", "country": "us", "capabilities": ["Linodes", "Backups", "NodeBalancers", - "Block Storage", "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", - "Vlans", "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": "2600:3c03::7, 2600:3c03::4, 2600:3c03::9, 2600:3c03::6, 2600:3c03::3, - 2600:3c03::c, 2600:3c03::5, 2600:3c03::b, 2600:3c03::2, 2600:3c03::8"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}, {"id": "eu-west", "label": "London, UK", "country": "gb", "capabilities": - ["Linodes", "Backups", "NodeBalancers", "Block Storage", "Kubernetes", "Cloud - Firewall", "Vlans", "Block Storage Migrations", "Managed Databases", "Metadata"], - "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": "2a01:7e00::9, 2a01:7e00::3, 2a01:7e00::c, 2a01:7e00::5, - 2a01:7e00::6, 2a01:7e00::8, 2a01:7e00::b, 2a01:7e00::4, 2a01:7e00::7, 2a01:7e00::2"}, - "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", "Block Storage", - "Object Storage", "GPU Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block - Storage Migrations", "Managed Databases", "Metadata"], "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": - "2400:8901::5, 2400:8901::4, 2400:8901::b, 2400:8901::3, 2400:8901::9, 2400:8901::2, - 2400:8901::8, 2400:8901::7, 2400:8901::c, 2400:8901::6"}, "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", "Block Storage", "Object Storage", "GPU - Linodes", "Kubernetes", "Cloud Firewall", "Vlans", "Block Storage Migrations", - "Managed Databases", "Metadata"], "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": "2a01:7e01::5, 2a01:7e01::9, - 2a01:7e01::7, 2a01:7e01::c, 2a01:7e01::2, 2a01:7e01::4, 2a01:7e01::3, 2a01:7e01::6, - 2a01:7e01::b, 2a01:7e01::8"}, "placement_group_limits": {"maximum_pgs_per_customer": - 100, "maximum_linodes_per_pg": 5}, "site_type": "core"}, {"id": "ap-northeast", - "label": "Tokyo, JP", "country": "jp", "capabilities": ["Linodes", "Backups", - "NodeBalancers", "Block Storage", "Kubernetes", "Cloud Firewall", "Vlans", "Block - Storage Migrations", "Managed Databases", "Metadata"], "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": - "2400:8902::3, 2400:8902::6, 2400:8902::c, 2400:8902::4, 2400:8902::2, 2400:8902::8, - 2400:8902::7, 2400:8902::5, 2400:8902::b, 2400:8902::9"}, "placement_group_limits": - {"maximum_pgs_per_customer": 100, "maximum_linodes_per_pg": 5}, "site_type": - "core"}], "page": 1, "pages": 1, "results": 25}' - 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: - - max-age=0, no-cache, no-store - Connection: - - keep-alive - Content-Security-Policy: - - default-src 'none' - Content-Type: - - application/json - Expires: - - Fri, 31 May 2024 17:09:38 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: - - account:read_write databases:read_write domains:read_write events:read_write - firewall:read_write images:read_write ips:read_write linodes:read_write lke:read_write - longview:read_write nodebalancers:read_write object_storage:read_write stackscripts:read_write - volumes:read_write vpc:read_write - X-Ratelimit-Limit: - - "400" - X-Xss-Protection: - - 1; mode=block - status: 200 OK - code: 200 - duration: "" -- request: - body: '{"region":"ap-west","label":"linodego-image-create-upload","description":"An - image that does stuff.","cloud_init":true}' + body: '{"region":"us-east","label":"linodego-image-create-upload","description":"An + image that does stuff.","cloud_init":true,"tags":["foo","bar"]}' form: {} headers: Accept: @@ -314,12 +15,13 @@ interactions: url: https://api.linode.com/v4beta/images/upload method: POST response: - body: '{"upload_to": "https://ap-south-1.linodeobjects.com:443/linode-production-machine-images-uploads/25476412?Signature=Tjr7JNsHgbSDhMZqmCJ%2F7hoOyt8%3D&Expires=1717261780&AWSAccessKeyID=SANITIZED", - "image": {"id": "private/25476412", "label": "linodego-image-create-upload", + body: '{"upload_to": "https://alpha.linodeobjects.com:443/linode-machine-images-upload/10044175?Signature=ef8tiIxbZuoAy%2BN%2BKO2jQ%2Bhg0yY%3D&Expires=1718393154&AWSAccessKeyID=SANITIZED", + "image": {"id": "private/10044175", "label": "linodego-image-create-upload", "description": "An image that does stuff.", "created": "2018-01-02T03:04:05", - "updated": "2018-01-02T03:04:05", "size": 0, "created_by": "youjungk01", "type": - "manual", "tags": [], "is_public": false, "deprecated": false, "vendor": null, - "expiry": null, "eol": null, "status": "pending_upload", "capabilities": ["cloud-init"]}}' + "updated": "2018-01-02T03:04:05", "size": 0, "created_by": "lgarber", "type": + "manual", "tags": ["bar", "foo"], "is_public": false, "deprecated": false, "vendor": + null, "expiry": null, "eol": null, "status": "pending_upload", "capabilities": + ["cloud-init"], "regions": [], "total_size": 0}}' headers: Access-Control-Allow-Credentials: - "true" @@ -332,22 +34,17 @@ interactions: Access-Control-Expose-Headers: - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status Cache-Control: - - max-age=0, no-cache, no-store - Connection: - - keep-alive - Content-Length: - - "610" + - private, max-age=60, s-maxage=60 Content-Security-Policy: - default-src 'none' Content-Type: - application/json - Expires: - - Fri, 31 May 2024 17:09:40 GMT - Pragma: - - no-cache + Server: + - nginx Strict-Transport-Security: - max-age=31536000 Vary: + - Accept-Encoding - Authorization, X-Filter X-Accepted-Oauth-Scopes: - images:read_write @@ -357,10 +54,7 @@ interactions: - DENY - DENY X-Oauth-Scopes: - - account:read_write databases:read_write domains:read_write events:read_write - firewall:read_write images:read_write ips:read_write linodes:read_write lke:read_write - longview:read_write nodebalancers:read_write object_storage:read_write stackscripts:read_write - volumes:read_write vpc:read_write + - '*' X-Ratelimit-Limit: - "400" X-Xss-Protection: @@ -378,7 +72,7 @@ interactions: - application/json User-Agent: - linodego/dev https://github.com/linode/linodego - url: https://api.linode.com/v4beta/images/private%2F25476412 + url: https://api.linode.com/v4beta/images/private%2F10044175 method: DELETE response: body: '{}' @@ -394,19 +88,15 @@ interactions: Access-Control-Expose-Headers: - X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status Cache-Control: - - max-age=0, no-cache, no-store - Connection: - - keep-alive + - private, max-age=60, s-maxage=60 Content-Length: - "2" Content-Security-Policy: - default-src 'none' Content-Type: - application/json - Expires: - - Fri, 31 May 2024 17:09:40 GMT - Pragma: - - no-cache + Server: + - nginx Strict-Transport-Security: - max-age=31536000 Vary: @@ -419,10 +109,7 @@ interactions: - DENY - DENY X-Oauth-Scopes: - - account:read_write databases:read_write domains:read_write events:read_write - firewall:read_write images:read_write ips:read_write linodes:read_write lke:read_write - longview:read_write nodebalancers:read_write object_storage:read_write stackscripts:read_write - volumes:read_write vpc:read_write + - '*' X-Ratelimit-Limit: - "400" X-Xss-Protection: diff --git a/test/integration/images_test.go b/test/integration/images_test.go index 785531d13..d162fb706 100644 --- a/test/integration/images_test.go +++ b/test/integration/images_test.go @@ -3,8 +3,13 @@ package integration import ( "bytes" "context" + "fmt" + "slices" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/dnaeon/go-vcr/recorder" . "github.com/linode/linodego" ) @@ -107,10 +112,14 @@ func TestImage_CreateUpload(t *testing.T) { defer teardown() image, uploadURL, err := client.CreateImageUpload(context.Background(), ImageCreateUploadOptions{ - Region: getRegionsWithCaps(t, client, []string{"Metadata"})[0], + // TODO: Uncomment before merging Images Gen. 2 support to main + //Region: getRegionsWithCaps(t, client, []string{"Metadata"})[0], + Region: "us-east", + Label: "linodego-image-create-upload", Description: "An image that does stuff.", CloudInit: true, + Tags: &[]string{"foo", "bar"}, }) if err != nil { t.Errorf("Failed to create image upload: %v", err) @@ -126,13 +135,17 @@ func TestImage_CreateUpload(t *testing.T) { if uploadURL == "" { t.Errorf("Expected upload URL, got none") } + + require.NotNil(t, image.Tags) } func TestImage_CloudInit(t *testing.T) { client, instance, teardown, err := setupInstance( t, "fixtures/TestImage_CloudInit", true, func(client *Client, options *InstanceCreateOptions) { - options.Region = getRegionsWithCaps(t, client, []string{"Metadata"})[0] + // TODO: Uncomment before merging Images Gen. 2 support to main + //options.Region = getRegionsWithCaps(t, client, []string{"Metadata"})[0] + options.Region = "us-east" }) if err != nil { t.Fatal(err) @@ -152,9 +165,10 @@ func TestImage_CloudInit(t *testing.T) { DiskID: instanceDisks[0].ID, Label: "linodego-test-cloud-init", CloudInit: true, + Tags: &[]string{"test1", "test2"}, }) if err != nil { - t.Errorf("Failed to create image upload: %v", err) + t.Errorf("Failed to create image: %v", err) } t.Cleanup(func() { if err := client.DeleteImage(context.Background(), image.ID); err != nil { @@ -163,4 +177,70 @@ func TestImage_CloudInit(t *testing.T) { }) assertSliceContains(t, image.Capabilities, "cloud-init") + + slices.Sort(image.Tags) + require.Equal(t, image.Tags, []string{"test1", "test2"}) +} + +func TestImage_Replicate(t *testing.T) { + // TODO: Remove when replication is available + t.Skip("Replication is not yet available") + + var testRegion string + var availableRegions []string + + client, instance, teardown, err := setupInstance( + t, "fixtures/TestImage_Replicate", true, + func(client *Client, options *InstanceCreateOptions) { + // TODO: Uncomment before merging Images Gen. 2 to main + // availableRegions = getRegionsWithCaps(t, client, []string{"Linodes"}) + availableRegions = []string{"us-east", "us-central", "us-southeast"} + + testRegion = availableRegions[0] + options.Region = testRegion + }) + require.NoError(t, err) + t.Cleanup(teardown) + + instanceDisks, err := client.ListInstanceDisks( + context.Background(), + instance.ID, + nil, + ) + require.NoError(t, err) + + image, err := client.CreateImage(context.Background(), ImageCreateOptions{ + DiskID: instanceDisks[0].ID, + Label: "linodego-test-replication", + }) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, client.DeleteImage(context.Background(), image.ID)) + }) + + image, err = client.WaitForImageStatus(context.Background(), image.ID, ImageStatusAvailable, 240) + require.NoError(t, err) + // + //require.Equal(t, testRegion, image.Regions[0].Region) + //require.NotZero(t, image.Regions[0].Status) + + image, err = client.ReplicateImage(context.Background(), image.ID, ImageReplicateOptions{ + Regions: availableRegions[1:], + }) + require.NoError(t, err) + + // Wait for the image to start replicating + image, err = client.WaitForImageStatus(context.Background(), image.ID, ImageStatusReplicating, 240) + require.NoError(t, err) + + // Wait for the replication process to complete + image, err = client.WaitForImageStatus(context.Background(), image.ID, ImageStatusAvailable, 240) + require.NoError(t, err) + + fmt.Println(image) + + require.Len(t, image.Regions, len(availableRegions)) + for _, region := range image.Regions { + assert.Contains(t, region.Region, availableRegions) + } } From dca4a480e28abe6d8dd40b2d73b76e1bbc95e5b6 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Tue, 25 Jun 2024 11:46:12 -0400 Subject: [PATCH 2/7] Add unit test case --- .gitignore | 2 ++ test/unit/images_test.go | 61 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 test/unit/images_test.go diff --git a/.gitignore b/.gitignore index cec47a064..ad0540950 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ vendor/**/ .env coverage.txt + +go.work.sum \ No newline at end of file diff --git a/test/unit/images_test.go b/test/unit/images_test.go new file mode 100644 index 000000000..839a0c34b --- /dev/null +++ b/test/unit/images_test.go @@ -0,0 +1,61 @@ +package unit + +import ( + "context" + "testing" + + "github.com/jarcoal/httpmock" + "github.com/linode/linodego" + "github.com/stretchr/testify/require" +) + +func TestImage_Replicate(t *testing.T) { + client := createMockClient(t) + + requestData := linodego.ImageReplicateOptions{ + Regions: []string{ + "us-mia", + "us-ord", + }, + } + + responseData := linodego.Image{ + ID: "private/1234", + Label: "test", + Regions: []linodego.ImageRegion{ + { + Region: "us-iad", + Status: linodego.ImageStatusAvailable, + }, + { + Region: "us-mia", + Status: linodego.ImageStatusReplicating, + }, + { + Region: "us-ord", + Status: linodego.ImageStatusPendingReplication, + }, + }, + } + + httpmock.RegisterRegexpResponder( + "POST", + mockRequestURL(t, "images/private%2F1234/regions"), + mockRequestBodyValidate(t, requestData, responseData), + ) + + image, err := client.ReplicateImage(context.Background(), "private/1234", requestData) + require.NoError(t, err) + + require.Equal(t, "private/1234", image.ID) + require.Equal(t, "test", image.Label) + + require.EqualValues(t, "us-iad", image.Regions[0].Region) + require.EqualValues(t, linodego.ImageStatusAvailable, image.Regions[0].Status) + + require.EqualValues(t, "us-mia", image.Regions[1].Region) + require.EqualValues(t, linodego.ImageStatusReplicating, image.Regions[1].Status) + + require.EqualValues(t, "us-ord", image.Regions[2].Region) + require.EqualValues(t, linodego.ImageStatusPendingReplication, image.Regions[2].Status) +} From 2cd09ed20fa983c69ffef5f79507a3e8eb4e63a0 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Tue, 25 Jun 2024 11:51:31 -0400 Subject: [PATCH 3/7] Drop go.work.sum from source tree --- go.work.sum | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 go.work.sum diff --git a/go.work.sum b/go.work.sum deleted file mode 100644 index e69de29bb..000000000 From 4a59223caa409015014d2b22b604921f10194929 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Tue, 25 Jun 2024 11:51:47 -0400 Subject: [PATCH 4/7] oops --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ad0540950..cd3a44d4a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,4 @@ vendor/**/ .env coverage.txt -go.work.sum \ No newline at end of file +go.work.sum From d07d0642ad1ce09016f893bffbf3590c0ab8384a Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Tue, 25 Jun 2024 12:23:34 -0400 Subject: [PATCH 5/7] Add tags to ImageUploadOptions --- images.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/images.go b/images.go index a26ad2045..a83139cdf 100644 --- a/images.go +++ b/images.go @@ -92,10 +92,11 @@ type ImageCreateUploadOptions struct { // ImageUploadOptions fields are those accepted by UploadImage type ImageUploadOptions struct { - Region string `json:"region"` - Label string `json:"label"` - Description string `json:"description,omitempty"` - CloudInit bool `json:"cloud_init"` + Region string `json:"region"` + Label string `json:"label"` + Description string `json:"description,omitempty"` + CloudInit bool `json:"cloud_init"` + Tags *[]string `json:"tags,omitempty"` Image io.Reader } @@ -227,6 +228,7 @@ func (c *Client) UploadImage(ctx context.Context, opts ImageUploadOptions) (*Ima Region: opts.Region, Description: opts.Description, CloudInit: opts.CloudInit, + Tags: opts.Tags, }) if err != nil { return nil, err From cfda66b5b88eb391f6a2cc1256db727c6ce56a64 Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 27 Jun 2024 10:27:42 -0400 Subject: [PATCH 6/7] Separate ImageStatus from ImageRegionStatus --- images.go | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/images.go b/images.go index a83139cdf..3c82e0fc6 100644 --- a/images.go +++ b/images.go @@ -15,19 +15,29 @@ type ImageStatus string // ImageStatus options start with ImageStatus and include all Image statuses const ( - ImageStatusAvailable ImageStatus = "available" - ImageStatusCreating ImageStatus = "creating" - ImageStatusPending ImageStatus = "pending" - ImageStatusPendingReplication ImageStatus = "pending replication" - ImageStatusPendingDeletion ImageStatus = "pending deletion" - ImageStatusPendingUpload ImageStatus = "pending_upload" - ImageStatusReplicating ImageStatus = "replicating" + ImageStatusCreating ImageStatus = "creating" + ImageStatusPendingUpload ImageStatus = "pending_upload" + ImageStatusAvailable ImageStatus = "available" +) + +// ImageRegionStatus represents the status of an Image's replication. +type ImageRegionStatus string + +// ImageRegionStatus options start with ImageRegionStatus and +// include all Image replication statuses +const ( + ImageRegionStatusAvailable ImageRegionStatus = "available" + ImageRegionStatusCreating ImageRegionStatus = "creating" + ImageRegionStatusPending ImageRegionStatus = "pending" + ImageRegionStatusPendingReplication ImageRegionStatus = "pending replication" + ImageRegionStatusPendingDeletion ImageRegionStatus = "pending deletion" + ImageRegionStatusReplicating ImageRegionStatus = "replicating" ) // ImageRegion represents the status of an Image object in a given Region. type ImageRegion struct { - Region string `json:"region"` - Status ImageStatus `json:"status"` + Region string `json:"region"` + Status ImageRegionStatus `json:"status"` } // Image represents a deployable Image object for use with Linode Instances From 02417029c84239b2b1150e873f48a633ac581c5e Mon Sep 17 00:00:00 2001 From: Lena Garber Date: Thu, 27 Jun 2024 10:51:08 -0400 Subject: [PATCH 7/7] Fix tests; add WaitForImageRegionStatus --- images.go | 4 ++-- test/integration/images_test.go | 27 +++++++++++-------------- test/integration/main_test.go | 3 ++- test/unit/images_test.go | 12 +++++------ waitfor.go | 35 +++++++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 25 deletions(-) diff --git a/images.go b/images.go index 3c82e0fc6..dd1d5f3fa 100644 --- a/images.go +++ b/images.go @@ -20,11 +20,11 @@ const ( ImageStatusAvailable ImageStatus = "available" ) -// ImageRegionStatus represents the status of an Image's replication. +// ImageRegionStatus represents the status of an Image's replica. type ImageRegionStatus string // ImageRegionStatus options start with ImageRegionStatus and -// include all Image replication statuses +// include all Image replica statuses const ( ImageRegionStatusAvailable ImageRegionStatus = "available" ImageRegionStatusCreating ImageRegionStatus = "creating" diff --git a/test/integration/images_test.go b/test/integration/images_test.go index d162fb706..a848c1049 100644 --- a/test/integration/images_test.go +++ b/test/integration/images_test.go @@ -3,7 +3,6 @@ package integration import ( "bytes" "context" - "fmt" "slices" "testing" @@ -113,7 +112,7 @@ func TestImage_CreateUpload(t *testing.T) { image, uploadURL, err := client.CreateImageUpload(context.Background(), ImageCreateUploadOptions{ // TODO: Uncomment before merging Images Gen. 2 support to main - //Region: getRegionsWithCaps(t, client, []string{"Metadata"})[0], + // Region: getRegionsWithCaps(t, client, []string{"Metadata"})[0], Region: "us-east", Label: "linodego-image-create-upload", @@ -144,7 +143,7 @@ func TestImage_CloudInit(t *testing.T) { t, "fixtures/TestImage_CloudInit", true, func(client *Client, options *InstanceCreateOptions) { // TODO: Uncomment before merging Images Gen. 2 support to main - //options.Region = getRegionsWithCaps(t, client, []string{"Metadata"})[0] + // options.Region = getRegionsWithCaps(t, client, []string{"Metadata"})[0] options.Region = "us-east" }) if err != nil { @@ -220,24 +219,20 @@ func TestImage_Replicate(t *testing.T) { image, err = client.WaitForImageStatus(context.Background(), image.ID, ImageStatusAvailable, 240) require.NoError(t, err) - // - //require.Equal(t, testRegion, image.Regions[0].Region) - //require.NotZero(t, image.Regions[0].Status) + + replicaRegions := availableRegions[1:] image, err = client.ReplicateImage(context.Background(), image.ID, ImageReplicateOptions{ - Regions: availableRegions[1:], + Regions: replicaRegions, }) require.NoError(t, err) - // Wait for the image to start replicating - image, err = client.WaitForImageStatus(context.Background(), image.ID, ImageStatusReplicating, 240) - require.NoError(t, err) - - // Wait for the replication process to complete - image, err = client.WaitForImageStatus(context.Background(), image.ID, ImageStatusAvailable, 240) - require.NoError(t, err) - - fmt.Println(image) + // Wait for all region replications to finish + for _, region := range replicaRegions { + image, err = client.WaitForImageRegionStatus( + context.Background(), image.ID, region, ImageRegionStatusAvailable, + ) + } require.Len(t, image.Regions, len(availableRegions)) for _, region := range image.Regions { diff --git a/test/integration/main_test.go b/test/integration/main_test.go index 9f23c9c88..8596e5dd8 100644 --- a/test/integration/main_test.go +++ b/test/integration/main_test.go @@ -3,13 +3,14 @@ package integration import ( "context" "fmt" - "github.com/linode/linodego" "io" "log" "net/http" "os" "testing" "time" + + "github.com/linode/linodego" ) var ( diff --git a/test/unit/images_test.go b/test/unit/images_test.go index 839a0c34b..d93353844 100644 --- a/test/unit/images_test.go +++ b/test/unit/images_test.go @@ -25,15 +25,15 @@ func TestImage_Replicate(t *testing.T) { Regions: []linodego.ImageRegion{ { Region: "us-iad", - Status: linodego.ImageStatusAvailable, + Status: linodego.ImageRegionStatusAvailable, }, { Region: "us-mia", - Status: linodego.ImageStatusReplicating, + Status: linodego.ImageRegionStatusReplicating, }, { Region: "us-ord", - Status: linodego.ImageStatusPendingReplication, + Status: linodego.ImageRegionStatusPendingReplication, }, }, } @@ -51,11 +51,11 @@ func TestImage_Replicate(t *testing.T) { require.Equal(t, "test", image.Label) require.EqualValues(t, "us-iad", image.Regions[0].Region) - require.EqualValues(t, linodego.ImageStatusAvailable, image.Regions[0].Status) + require.EqualValues(t, linodego.ImageRegionStatusAvailable, image.Regions[0].Status) require.EqualValues(t, "us-mia", image.Regions[1].Region) - require.EqualValues(t, linodego.ImageStatusReplicating, image.Regions[1].Status) + require.EqualValues(t, linodego.ImageRegionStatusReplicating, image.Regions[1].Status) require.EqualValues(t, "us-ord", image.Regions[2].Region) - require.EqualValues(t, linodego.ImageStatusPendingReplication, image.Regions[2].Status) + require.EqualValues(t, linodego.ImageRegionStatusPendingReplication, image.Regions[2].Status) } diff --git a/waitfor.go b/waitfor.go index 0b4d6c7bd..df2f484f7 100644 --- a/waitfor.go +++ b/waitfor.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "net/http" + "slices" "strconv" "time" @@ -442,6 +443,40 @@ func (client Client) WaitForImageStatus(ctx context.Context, imageID string, sta } } +// WaitForImageRegionStatus waits for an Image's replica to reach the desired state +// before returning. +func (client Client) WaitForImageRegionStatus(ctx context.Context, imageID, region string, status ImageRegionStatus) (*Image, error) { + ticker := time.NewTicker(client.pollInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + image, err := client.GetImage(ctx, imageID) + if err != nil { + return image, err + } + + replicaIdx := slices.IndexFunc( + image.Regions, + func(r ImageRegion) bool { + return r.Region == region + }, + ) + + // If no replica was found or the status doesn't match, try again + if replicaIdx < 0 || image.Regions[replicaIdx].Status != status { + continue + } + + return image, nil + + case <-ctx.Done(): + return nil, fmt.Errorf("failed to wait for Image %s status %s: %w", imageID, status, ctx.Err()) + } + } +} + // WaitForMySQLDatabaseBackup waits for the backup with the given label to be available. func (client Client) WaitForMySQLDatabaseBackup(ctx context.Context, dbID int, label string, timeoutSeconds int) (*MySQLDatabaseBackup, error) { ctx, cancel := context.WithTimeout(ctx, time.Duration(timeoutSeconds)*time.Second)