Skip to content

Commit

Permalink
Project: Images Service Gen2 (#560)
Browse files Browse the repository at this point in the history
* new: Add support for Images Gen. 2 (#526)

* WIP
oops

* Add unit test case

* Drop go.work.sum from source tree

* oops

* Add tags to ImageUploadOptions

* Separate ImageStatus from ImageRegionStatus

* Fix tests; add WaitForImageRegionStatus

* image replication test with upload image; use getRegionsWithCaps when available

* modified replication tests to use uploaded image; use random regions when available

* fix fixtures

* clean up

* sanitize fixture

* revert ipv6 pool fixture change

---------

Co-authored-by: Lena Garber <[email protected]>
  • Loading branch information
yec-akamai and lgarber-akamai authored Aug 9, 2024
1 parent a405118 commit 7f1781b
Show file tree
Hide file tree
Showing 76 changed files with 11,919 additions and 8,108 deletions.
119 changes: 82 additions & 37 deletions images.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,69 @@ const (
ImageStatusAvailable ImageStatus = "available"
)

// ImageRegionStatus represents the status of an Image's replica.
type ImageRegionStatus string

// ImageRegionStatus options start with ImageRegionStatus and
// include all Image replica 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 ImageRegionStatus `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
Expand All @@ -61,18 +93,20 @@ 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
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
}

Expand Down Expand Up @@ -109,7 +143,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,
Expand All @@ -119,7 +153,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,
Expand All @@ -128,7 +162,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,
Expand All @@ -138,7 +172,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,
Expand All @@ -148,7 +182,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,
Expand All @@ -157,7 +201,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,
Expand All @@ -172,7 +216,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().
Expand All @@ -187,13 +231,14 @@ 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,
Region: opts.Region,
Description: opts.Description,
CloudInit: opts.CloudInit,
Tags: opts.Tags,
})
if err != nil {
return nil, err
Expand Down
9 changes: 7 additions & 2 deletions test/integration/fixtures/TestAccountAvailability_Get.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ interactions:
- '*'
Access-Control-Expose-Headers:
- X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status
Akamai-Internal-Account:
- '*'
Cache-Control:
- max-age=0, no-cache, no-store
Connection:
Expand All @@ -38,7 +40,7 @@ interactions:
Content-Type:
- application/json
Expires:
- Mon, 17 Jun 2024 15:46:53 GMT
- Thu, 25 Jul 2024 17:44:02 GMT
Pragma:
- no-cache
Strict-Transport-Security:
Expand All @@ -54,7 +56,10 @@ 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:
Expand Down
32 changes: 24 additions & 8 deletions test/integration/fixtures/TestAccountAvailability_List.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ interactions:
["Linodes", "NodeBalancers", "Block Storage", "Kubernetes"], "unavailable":
[]}, {"region": "ap-southeast", "available": ["Linodes", "NodeBalancers", "Block
Storage", "Kubernetes"], "unavailable": []}, {"region": "us-iad", "available":
["Linodes", "NodeBalancers", "Block Storage", "Kubernetes"], "unavailable":
[]}, {"region": "us-ord", "available": ["Linodes", "NodeBalancers", "Block Storage",
["Linodes", "NodeBalancers", "Block Storage"], "unavailable": ["Kubernetes"]},
{"region": "us-ord", "available": ["Linodes", "NodeBalancers", "Block Storage",
"Kubernetes"], "unavailable": []}, {"region": "fr-par", "available": ["Linodes",
"NodeBalancers", "Block Storage", "Kubernetes"], "unavailable": []}, {"region":
"us-sea", "available": ["Linodes", "NodeBalancers", "Block Storage", "Kubernetes"],
Expand All @@ -50,10 +50,21 @@ interactions:
"Kubernetes"], "unavailable": []}, {"region": "id-cgk", "available": ["Linodes",
"NodeBalancers", "Block Storage", "Kubernetes"], "unavailable": []}, {"region":
"us-lax", "available": ["Linodes", "NodeBalancers", "Block Storage", "Kubernetes"],
"unavailable": []}, {"region": "gb-lon", "available": ["Linodes", "NodeBalancers",
"Block Storage", "Kubernetes"], "unavailable": []}, {"region": "au-mel", "available":
["Linodes", "NodeBalancers", "Block Storage", "Kubernetes"], "unavailable":
[]}], "page": 1, "pages": 1, "results": 27}'
"unavailable": []}, {"region": "us-den-edge-1", "available": ["Linodes"], "unavailable":
["NodeBalancers", "Block Storage", "Kubernetes"]}, {"region": "de-ham-edge-1",
"available": ["Linodes"], "unavailable": ["NodeBalancers", "Block Storage",
"Kubernetes"]}, {"region": "fr-mrs-edge-1", "available": ["Linodes"], "unavailable":
["NodeBalancers", "Block Storage", "Kubernetes"]}, {"region": "za-jnb-edge-1",
"available": ["Linodes"], "unavailable": ["NodeBalancers", "Block Storage",
"Kubernetes"]}, {"region": "my-kul-edge-1", "available": ["Linodes"], "unavailable":
["NodeBalancers", "Block Storage", "Kubernetes"]}, {"region": "co-bog-edge-1",
"available": ["Linodes"], "unavailable": ["NodeBalancers", "Block Storage",
"Kubernetes"]}, {"region": "mx-qro-edge-1", "available": ["Linodes"], "unavailable":
["NodeBalancers", "Block Storage", "Kubernetes"]}, {"region": "us-hou-edge-1",
"available": ["Linodes"], "unavailable": ["NodeBalancers", "Block Storage",
"Kubernetes"]}, {"region": "cl-scl-edge-1", "available": ["Linodes"], "unavailable":
["NodeBalancers", "Block Storage", "Kubernetes"]}], "page": 1, "pages": 1, "results":
34}'
headers:
Access-Control-Allow-Credentials:
- "true"
Expand All @@ -65,6 +76,8 @@ interactions:
- '*'
Access-Control-Expose-Headers:
- X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Status
Akamai-Internal-Account:
- '*'
Cache-Control:
- max-age=0, no-cache, no-store
Connection:
Expand All @@ -74,7 +87,7 @@ interactions:
Content-Type:
- application/json
Expires:
- Mon, 17 Jun 2024 15:46:53 GMT
- Thu, 25 Jul 2024 17:44:02 GMT
Pragma:
- no-cache
Strict-Transport-Security:
Expand All @@ -91,7 +104,10 @@ 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:
Expand Down
Loading

0 comments on commit 7f1781b

Please sign in to comment.