2016-07-11 13:09:06 +02:00
|
|
|
package godo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
2017-05-15 15:54:16 +02:00
|
|
|
|
|
|
|
"github.com/digitalocean/godo/context"
|
2016-07-11 13:09:06 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
storageBasePath = "v2"
|
|
|
|
storageAllocPath = storageBasePath + "/volumes"
|
|
|
|
storageSnapPath = storageBasePath + "/snapshots"
|
|
|
|
)
|
|
|
|
|
|
|
|
// StorageService is an interface for interfacing with the storage
|
|
|
|
// endpoints of the Digital Ocean API.
|
|
|
|
// See: https://developers.digitalocean.com/documentation/v2#storage
|
|
|
|
type StorageService interface {
|
2017-05-15 15:54:16 +02:00
|
|
|
ListVolumes(context.Context, *ListVolumeParams) ([]Volume, *Response, error)
|
|
|
|
GetVolume(context.Context, string) (*Volume, *Response, error)
|
|
|
|
CreateVolume(context.Context, *VolumeCreateRequest) (*Volume, *Response, error)
|
|
|
|
DeleteVolume(context.Context, string) (*Response, error)
|
|
|
|
ListSnapshots(ctx context.Context, volumeID string, opts *ListOptions) ([]Snapshot, *Response, error)
|
|
|
|
GetSnapshot(context.Context, string) (*Snapshot, *Response, error)
|
|
|
|
CreateSnapshot(context.Context, *SnapshotCreateRequest) (*Snapshot, *Response, error)
|
|
|
|
DeleteSnapshot(context.Context, string) (*Response, error)
|
2016-07-11 13:09:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// StorageServiceOp handles communication with the storage volumes related methods of the
|
|
|
|
// DigitalOcean API.
|
|
|
|
type StorageServiceOp struct {
|
|
|
|
client *Client
|
|
|
|
}
|
|
|
|
|
2017-05-15 15:54:16 +02:00
|
|
|
// ListVolumeParams stores the options you can set for a ListVolumeCall
|
|
|
|
type ListVolumeParams struct {
|
|
|
|
Region string `json:"region"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
ListOptions *ListOptions `json:"list_options,omitempty"`
|
|
|
|
}
|
|
|
|
|
2016-07-11 13:09:06 +02:00
|
|
|
var _ StorageService = &StorageServiceOp{}
|
|
|
|
|
|
|
|
// Volume represents a Digital Ocean block store volume.
|
|
|
|
type Volume struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Region *Region `json:"region"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
SizeGigaBytes int64 `json:"size_gigabytes"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
DropletIDs []int `json:"droplet_ids"`
|
|
|
|
CreatedAt time.Time `json:"created_at"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f Volume) String() string {
|
|
|
|
return Stringify(f)
|
|
|
|
}
|
|
|
|
|
|
|
|
type storageVolumesRoot struct {
|
|
|
|
Volumes []Volume `json:"volumes"`
|
|
|
|
Links *Links `json:"links"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type storageVolumeRoot struct {
|
|
|
|
Volume *Volume `json:"volume"`
|
|
|
|
Links *Links `json:"links,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// VolumeCreateRequest represents a request to create a block store
|
|
|
|
// volume.
|
|
|
|
type VolumeCreateRequest struct {
|
|
|
|
Region string `json:"region"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
SizeGigaBytes int64 `json:"size_gigabytes"`
|
2017-05-15 15:54:16 +02:00
|
|
|
SnapshotID string `json:"snapshot_id"`
|
2016-07-11 13:09:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListVolumes lists all storage volumes.
|
2017-05-15 15:54:16 +02:00
|
|
|
func (svc *StorageServiceOp) ListVolumes(ctx context.Context, params *ListVolumeParams) ([]Volume, *Response, error) {
|
|
|
|
path := storageAllocPath
|
|
|
|
if params != nil {
|
|
|
|
if params.Region != "" && params.Name != "" {
|
|
|
|
path = fmt.Sprintf("%s?name=%s®ion=%s", path, params.Name, params.Region)
|
|
|
|
}
|
|
|
|
|
|
|
|
if params.ListOptions != nil {
|
|
|
|
var err error
|
|
|
|
path, err = addOptions(path, params.ListOptions)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
}
|
2016-07-11 13:09:06 +02:00
|
|
|
}
|
|
|
|
|
2017-05-15 15:54:16 +02:00
|
|
|
req, err := svc.client.NewRequest(ctx, "GET", path, nil)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
root := new(storageVolumesRoot)
|
2017-05-15 15:54:16 +02:00
|
|
|
resp, err := svc.client.Do(ctx, req, root)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if l := root.Links; l != nil {
|
|
|
|
resp.Links = l
|
|
|
|
}
|
|
|
|
|
|
|
|
return root.Volumes, resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateVolume creates a storage volume. The name must be unique.
|
2017-05-15 15:54:16 +02:00
|
|
|
func (svc *StorageServiceOp) CreateVolume(ctx context.Context, createRequest *VolumeCreateRequest) (*Volume, *Response, error) {
|
2016-07-11 13:09:06 +02:00
|
|
|
path := storageAllocPath
|
|
|
|
|
2017-05-15 15:54:16 +02:00
|
|
|
req, err := svc.client.NewRequest(ctx, "POST", path, createRequest)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
root := new(storageVolumeRoot)
|
2017-05-15 15:54:16 +02:00
|
|
|
resp, err := svc.client.Do(ctx, req, root)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
return root.Volume, resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetVolume retrieves an individual storage volume.
|
2017-05-15 15:54:16 +02:00
|
|
|
func (svc *StorageServiceOp) GetVolume(ctx context.Context, id string) (*Volume, *Response, error) {
|
2016-07-11 13:09:06 +02:00
|
|
|
path := fmt.Sprintf("%s/%s", storageAllocPath, id)
|
|
|
|
|
2017-05-15 15:54:16 +02:00
|
|
|
req, err := svc.client.NewRequest(ctx, "GET", path, nil)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
root := new(storageVolumeRoot)
|
2017-05-15 15:54:16 +02:00
|
|
|
resp, err := svc.client.Do(ctx, req, root)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return root.Volume, resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteVolume deletes a storage volume.
|
2017-05-15 15:54:16 +02:00
|
|
|
func (svc *StorageServiceOp) DeleteVolume(ctx context.Context, id string) (*Response, error) {
|
2016-07-11 13:09:06 +02:00
|
|
|
path := fmt.Sprintf("%s/%s", storageAllocPath, id)
|
|
|
|
|
2017-05-15 15:54:16 +02:00
|
|
|
req, err := svc.client.NewRequest(ctx, "DELETE", path, nil)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-05-15 15:54:16 +02:00
|
|
|
return svc.client.Do(ctx, req, nil)
|
2016-07-11 13:09:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// SnapshotCreateRequest represents a request to create a block store
|
|
|
|
// volume.
|
|
|
|
type SnapshotCreateRequest struct {
|
|
|
|
VolumeID string `json:"volume_id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListSnapshots lists all snapshots related to a storage volume.
|
2017-05-15 15:54:16 +02:00
|
|
|
func (svc *StorageServiceOp) ListSnapshots(ctx context.Context, volumeID string, opt *ListOptions) ([]Snapshot, *Response, error) {
|
2016-07-11 13:09:06 +02:00
|
|
|
path := fmt.Sprintf("%s/%s/snapshots", storageAllocPath, volumeID)
|
|
|
|
path, err := addOptions(path, opt)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2017-05-15 15:54:16 +02:00
|
|
|
req, err := svc.client.NewRequest(ctx, "GET", path, nil)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2017-02-15 15:35:41 +01:00
|
|
|
root := new(snapshotsRoot)
|
2017-05-15 15:54:16 +02:00
|
|
|
resp, err := svc.client.Do(ctx, req, root)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if l := root.Links; l != nil {
|
|
|
|
resp.Links = l
|
|
|
|
}
|
|
|
|
|
|
|
|
return root.Snapshots, resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateSnapshot creates a snapshot of a storage volume.
|
2017-05-15 15:54:16 +02:00
|
|
|
func (svc *StorageServiceOp) CreateSnapshot(ctx context.Context, createRequest *SnapshotCreateRequest) (*Snapshot, *Response, error) {
|
2016-07-11 13:09:06 +02:00
|
|
|
path := fmt.Sprintf("%s/%s/snapshots", storageAllocPath, createRequest.VolumeID)
|
|
|
|
|
2017-05-15 15:54:16 +02:00
|
|
|
req, err := svc.client.NewRequest(ctx, "POST", path, createRequest)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2017-02-15 15:35:41 +01:00
|
|
|
root := new(snapshotRoot)
|
2017-05-15 15:54:16 +02:00
|
|
|
resp, err := svc.client.Do(ctx, req, root)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
return root.Snapshot, resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetSnapshot retrieves an individual snapshot.
|
2017-05-15 15:54:16 +02:00
|
|
|
func (svc *StorageServiceOp) GetSnapshot(ctx context.Context, id string) (*Snapshot, *Response, error) {
|
2016-07-11 13:09:06 +02:00
|
|
|
path := fmt.Sprintf("%s/%s", storageSnapPath, id)
|
|
|
|
|
2017-05-15 15:54:16 +02:00
|
|
|
req, err := svc.client.NewRequest(ctx, "GET", path, nil)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2017-02-15 15:35:41 +01:00
|
|
|
root := new(snapshotRoot)
|
2017-05-15 15:54:16 +02:00
|
|
|
resp, err := svc.client.Do(ctx, req, root)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, resp, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return root.Snapshot, resp, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteSnapshot deletes a snapshot.
|
2017-05-15 15:54:16 +02:00
|
|
|
func (svc *StorageServiceOp) DeleteSnapshot(ctx context.Context, id string) (*Response, error) {
|
2016-07-11 13:09:06 +02:00
|
|
|
path := fmt.Sprintf("%s/%s", storageSnapPath, id)
|
|
|
|
|
2017-05-15 15:54:16 +02:00
|
|
|
req, err := svc.client.NewRequest(ctx, "DELETE", path, nil)
|
2016-07-11 13:09:06 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-05-15 15:54:16 +02:00
|
|
|
return svc.client.Do(ctx, req, nil)
|
2016-07-11 13:09:06 +02:00
|
|
|
}
|