435c0d9fc8
This PR switches the Nomad repository from using govendor to Go modules for managing dependencies. Aspects of the Nomad workflow remain pretty much the same. The usual Makefile targets should continue to work as they always did. The API submodule simply defers to the parent Nomad version on the repository, keeping the semantics of API versioning that currently exists.
300 lines
7.6 KiB
Go
300 lines
7.6 KiB
Go
package linodego
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// VolumeStatus indicates the status of the Volume
|
|
type VolumeStatus string
|
|
|
|
const (
|
|
// VolumeCreating indicates the Volume is being created and is not yet available for use
|
|
VolumeCreating VolumeStatus = "creating"
|
|
|
|
// VolumeActive indicates the Volume is online and available for use
|
|
VolumeActive VolumeStatus = "active"
|
|
|
|
// VolumeResizing indicates the Volume is in the process of upgrading its current capacity
|
|
VolumeResizing VolumeStatus = "resizing"
|
|
|
|
// VolumeContactSupport indicates there is a problem with the Volume. A support ticket must be opened to resolve the issue
|
|
VolumeContactSupport VolumeStatus = "contact_support"
|
|
)
|
|
|
|
// Volume represents a linode volume object
|
|
type Volume struct {
|
|
CreatedStr string `json:"created"`
|
|
UpdatedStr string `json:"updated"`
|
|
|
|
ID int `json:"id"`
|
|
Label string `json:"label"`
|
|
Status VolumeStatus `json:"status"`
|
|
Region string `json:"region"`
|
|
Size int `json:"size"`
|
|
LinodeID *int `json:"linode_id"`
|
|
FilesystemPath string `json:"filesystem_path"`
|
|
Tags []string `json:"tags"`
|
|
Created time.Time `json:"-"`
|
|
Updated time.Time `json:"-"`
|
|
}
|
|
|
|
// VolumeCreateOptions fields are those accepted by CreateVolume
|
|
type VolumeCreateOptions struct {
|
|
Label string `json:"label,omitempty"`
|
|
Region string `json:"region,omitempty"`
|
|
LinodeID int `json:"linode_id,omitempty"`
|
|
ConfigID int `json:"config_id,omitempty"`
|
|
// The Volume's size, in GiB. Minimum size is 10GiB, maximum size is 10240GiB. A "0" value will result in the default size.
|
|
Size int `json:"size,omitempty"`
|
|
// An array of tags applied to this object. Tags are for organizational purposes only.
|
|
Tags []string `json:"tags"`
|
|
}
|
|
|
|
// VolumeUpdateOptions fields are those accepted by UpdateVolume
|
|
type VolumeUpdateOptions struct {
|
|
Label string `json:"label,omitempty"`
|
|
Tags *[]string `json:"tags,omitempty"`
|
|
}
|
|
|
|
// VolumeAttachOptions fields are those accepted by AttachVolume
|
|
type VolumeAttachOptions struct {
|
|
LinodeID int `json:"linode_id"`
|
|
ConfigID int `json:"config_id,omitempty"`
|
|
}
|
|
|
|
// VolumesPagedResponse represents a linode API response for listing of volumes
|
|
type VolumesPagedResponse struct {
|
|
*PageOptions
|
|
Data []Volume `json:"data"`
|
|
}
|
|
|
|
// GetUpdateOptions converts a Volume to VolumeUpdateOptions for use in UpdateVolume
|
|
func (v Volume) GetUpdateOptions() (updateOpts VolumeUpdateOptions) {
|
|
updateOpts.Label = v.Label
|
|
updateOpts.Tags = &v.Tags
|
|
return
|
|
}
|
|
|
|
// GetCreateOptions converts a Volume to VolumeCreateOptions for use in CreateVolume
|
|
func (v Volume) GetCreateOptions() (createOpts VolumeCreateOptions) {
|
|
createOpts.Label = v.Label
|
|
createOpts.Tags = v.Tags
|
|
createOpts.Region = v.Region
|
|
createOpts.Size = v.Size
|
|
if v.LinodeID != nil && *v.LinodeID > 0 {
|
|
createOpts.LinodeID = *v.LinodeID
|
|
}
|
|
return
|
|
}
|
|
|
|
// endpoint gets the endpoint URL for Volume
|
|
func (VolumesPagedResponse) endpoint(c *Client) string {
|
|
endpoint, err := c.Volumes.Endpoint()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return endpoint
|
|
}
|
|
|
|
// appendData appends Volumes when processing paginated Volume responses
|
|
func (resp *VolumesPagedResponse) appendData(r *VolumesPagedResponse) {
|
|
resp.Data = append(resp.Data, r.Data...)
|
|
}
|
|
|
|
// ListVolumes lists Volumes
|
|
func (c *Client) ListVolumes(ctx context.Context, opts *ListOptions) ([]Volume, error) {
|
|
response := VolumesPagedResponse{}
|
|
err := c.listHelper(ctx, &response, opts)
|
|
for i := range response.Data {
|
|
response.Data[i].fixDates()
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return response.Data, nil
|
|
}
|
|
|
|
// fixDates converts JSON timestamps to Go time.Time values
|
|
func (v *Volume) fixDates() *Volume {
|
|
if parsed, err := parseDates(v.CreatedStr); err != nil {
|
|
v.Created = *parsed
|
|
}
|
|
if parsed, err := parseDates(v.UpdatedStr); err != nil {
|
|
v.Updated = *parsed
|
|
}
|
|
return v
|
|
}
|
|
|
|
// GetVolume gets the template with the provided ID
|
|
func (c *Client) GetVolume(ctx context.Context, id int) (*Volume, error) {
|
|
e, err := c.Volumes.Endpoint()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e = fmt.Sprintf("%s/%d", e, id)
|
|
r, err := coupleAPIErrors(c.R(ctx).SetResult(&Volume{}).Get(e))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return r.Result().(*Volume).fixDates(), nil
|
|
}
|
|
|
|
// AttachVolume attaches a volume to a Linode instance
|
|
func (c *Client) AttachVolume(ctx context.Context, id int, options *VolumeAttachOptions) (*Volume, error) {
|
|
body := ""
|
|
if bodyData, err := json.Marshal(options); err == nil {
|
|
body = string(bodyData)
|
|
} else {
|
|
return nil, NewError(err)
|
|
}
|
|
|
|
e, err := c.Volumes.Endpoint()
|
|
if err != nil {
|
|
return nil, NewError(err)
|
|
}
|
|
|
|
e = fmt.Sprintf("%s/%d/attach", e, id)
|
|
resp, err := coupleAPIErrors(c.R(ctx).
|
|
SetResult(&Volume{}).
|
|
SetBody(body).
|
|
Post(e))
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return resp.Result().(*Volume).fixDates(), nil
|
|
}
|
|
|
|
// CreateVolume creates a Linode Volume
|
|
func (c *Client) CreateVolume(ctx context.Context, createOpts VolumeCreateOptions) (*Volume, error) {
|
|
body := ""
|
|
if bodyData, err := json.Marshal(createOpts); err == nil {
|
|
body = string(bodyData)
|
|
} else {
|
|
return nil, NewError(err)
|
|
}
|
|
|
|
e, err := c.Volumes.Endpoint()
|
|
if err != nil {
|
|
return nil, NewError(err)
|
|
}
|
|
|
|
resp, err := coupleAPIErrors(c.R(ctx).
|
|
SetResult(&Volume{}).
|
|
SetBody(body).
|
|
Post(e))
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return resp.Result().(*Volume).fixDates(), nil
|
|
}
|
|
|
|
// RenameVolume renames the label of a Linode volume
|
|
// DEPRECATED: use UpdateVolume
|
|
func (c *Client) RenameVolume(ctx context.Context, id int, label string) (*Volume, error) {
|
|
updateOpts := VolumeUpdateOptions{Label: label}
|
|
return c.UpdateVolume(ctx, id, updateOpts)
|
|
}
|
|
|
|
// UpdateVolume updates the Volume with the specified id
|
|
func (c *Client) UpdateVolume(ctx context.Context, id int, volume VolumeUpdateOptions) (*Volume, error) {
|
|
var body string
|
|
e, err := c.Volumes.Endpoint()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
e = fmt.Sprintf("%s/%d", e, id)
|
|
|
|
req := c.R(ctx).SetResult(&Volume{})
|
|
|
|
if bodyData, err := json.Marshal(volume); err == nil {
|
|
body = string(bodyData)
|
|
} else {
|
|
return nil, NewError(err)
|
|
}
|
|
|
|
r, err := coupleAPIErrors(req.
|
|
SetBody(body).
|
|
Put(e))
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return r.Result().(*Volume).fixDates(), nil
|
|
}
|
|
|
|
// CloneVolume clones a Linode volume
|
|
func (c *Client) CloneVolume(ctx context.Context, id int, label string) (*Volume, error) {
|
|
body := fmt.Sprintf("{\"label\":\"%s\"}", label)
|
|
|
|
e, err := c.Volumes.Endpoint()
|
|
if err != nil {
|
|
return nil, NewError(err)
|
|
}
|
|
e = fmt.Sprintf("%s/%d/clone", e, id)
|
|
|
|
resp, err := coupleAPIErrors(c.R(ctx).
|
|
SetResult(&Volume{}).
|
|
SetBody(body).
|
|
Post(e))
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return resp.Result().(*Volume).fixDates(), nil
|
|
}
|
|
|
|
// DetachVolume detaches a Linode volume
|
|
func (c *Client) DetachVolume(ctx context.Context, id int) error {
|
|
body := ""
|
|
|
|
e, err := c.Volumes.Endpoint()
|
|
if err != nil {
|
|
return NewError(err)
|
|
}
|
|
|
|
e = fmt.Sprintf("%s/%d/detach", e, id)
|
|
|
|
_, err = coupleAPIErrors(c.R(ctx).
|
|
SetBody(body).
|
|
Post(e))
|
|
|
|
return err
|
|
}
|
|
|
|
// ResizeVolume resizes an instance to new Linode type
|
|
func (c *Client) ResizeVolume(ctx context.Context, id int, size int) error {
|
|
body := fmt.Sprintf("{\"size\": %d}", size)
|
|
|
|
e, err := c.Volumes.Endpoint()
|
|
if err != nil {
|
|
return NewError(err)
|
|
}
|
|
e = fmt.Sprintf("%s/%d/resize", e, id)
|
|
|
|
_, err = coupleAPIErrors(c.R(ctx).
|
|
SetBody(body).
|
|
Post(e))
|
|
|
|
return err
|
|
}
|
|
|
|
// DeleteVolume deletes the Volume with the specified id
|
|
func (c *Client) DeleteVolume(ctx context.Context, id int) error {
|
|
e, err := c.Volumes.Endpoint()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e = fmt.Sprintf("%s/%d", e, id)
|
|
|
|
_, err = coupleAPIErrors(c.R(ctx).Delete(e))
|
|
return err
|
|
}
|