restore services expected by terraform provider
This commit is contained in:
parent
0646c0fc9d
commit
a788107028
6
godo.go
6
godo.go
|
@ -56,6 +56,9 @@ type Client struct {
|
|||
Keys KeysService
|
||||
Regions RegionsService
|
||||
Sizes SizesService
|
||||
Snapshots SnapshotsService
|
||||
Storage StorageService
|
||||
StorageActions StorageActionsService
|
||||
Tags TagsService
|
||||
|
||||
// Optional function called after every successful request made to the DO APIs
|
||||
|
@ -194,6 +197,9 @@ func NewClient(httpClient *http.Client) *Client {
|
|||
c.Keys = &KeysServiceOp{client: c}
|
||||
c.Regions = &RegionsServiceOp{client: c}
|
||||
c.Sizes = &SizesServiceOp{client: c}
|
||||
c.Snapshots = &SnapshotsServiceOp{client: c}
|
||||
c.Storage = &StorageServiceOp{client: c}
|
||||
c.StorageActions = &StorageActionsServiceOp{client: c}
|
||||
c.Tags = &TagsServiceOp{client: c}
|
||||
|
||||
c.headers = make(map[string]string)
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
package godo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const snapshotBasePath = "v2/snapshots"
|
||||
|
||||
// SnapshotsService is an interface for interfacing with the snapshots
|
||||
// endpoints of the DigitalOcean API
|
||||
// See: https://docs.digitalocean.com/reference/api/api-reference/#tag/Snapshots
|
||||
type SnapshotsService interface {
|
||||
List(context.Context, *ListOptions) ([]Snapshot, *Response, error)
|
||||
ListVolume(context.Context, *ListOptions) ([]Snapshot, *Response, error)
|
||||
ListDroplet(context.Context, *ListOptions) ([]Snapshot, *Response, error)
|
||||
Get(context.Context, string) (*Snapshot, *Response, error)
|
||||
Delete(context.Context, string) (*Response, error)
|
||||
}
|
||||
|
||||
// SnapshotsServiceOp handles communication with the snapshot related methods of the
|
||||
// DigitalOcean API.
|
||||
type SnapshotsServiceOp struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
var _ SnapshotsService = &SnapshotsServiceOp{}
|
||||
|
||||
// Snapshot represents a DigitalOcean Snapshot
|
||||
type Snapshot struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ResourceID string `json:"resource_id,omitempty"`
|
||||
ResourceType string `json:"resource_type,omitempty"`
|
||||
Regions []string `json:"regions,omitempty"`
|
||||
MinDiskSize int `json:"min_disk_size,omitempty"`
|
||||
SizeGigaBytes float64 `json:"size_gigabytes,omitempty"`
|
||||
Created string `json:"created_at,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
type snapshotRoot struct {
|
||||
Snapshot *Snapshot `json:"snapshot"`
|
||||
}
|
||||
|
||||
type snapshotsRoot struct {
|
||||
Snapshots []Snapshot `json:"snapshots"`
|
||||
Meta *Meta `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
type listSnapshotOptions struct {
|
||||
ResourceType string `url:"resource_type,omitempty"`
|
||||
}
|
||||
|
||||
func (s Snapshot) String() string {
|
||||
return Stringify(s)
|
||||
}
|
||||
|
||||
// List lists all the snapshots available.
|
||||
func (s *SnapshotsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||
return s.list(ctx, opt, nil)
|
||||
}
|
||||
|
||||
// ListDroplet lists all the Droplet snapshots.
|
||||
func (s *SnapshotsServiceOp) ListDroplet(ctx context.Context, opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||
listOpt := listSnapshotOptions{ResourceType: "droplet"}
|
||||
return s.list(ctx, opt, &listOpt)
|
||||
}
|
||||
|
||||
// ListVolume lists all the volume snapshots.
|
||||
func (s *SnapshotsServiceOp) ListVolume(ctx context.Context, opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||
listOpt := listSnapshotOptions{ResourceType: "volume"}
|
||||
return s.list(ctx, opt, &listOpt)
|
||||
}
|
||||
|
||||
// Get retrieves a snapshot by id.
|
||||
func (s *SnapshotsServiceOp) Get(ctx context.Context, snapshotID string) (*Snapshot, *Response, error) {
|
||||
return s.get(ctx, snapshotID)
|
||||
}
|
||||
|
||||
// Delete an snapshot.
|
||||
func (s *SnapshotsServiceOp) Delete(ctx context.Context, snapshotID string) (*Response, error) {
|
||||
path := fmt.Sprintf("%s/%s", snapshotBasePath, snapshotID)
|
||||
|
||||
req, err := s.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(ctx, req, nil)
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// Helper method for getting an individual snapshot
|
||||
func (s *SnapshotsServiceOp) get(ctx context.Context, ID string) (*Snapshot, *Response, error) {
|
||||
path := fmt.Sprintf("%s/%s", snapshotBasePath, ID)
|
||||
|
||||
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(snapshotRoot)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Snapshot, resp, err
|
||||
}
|
||||
|
||||
// Helper method for listing snapshots
|
||||
func (s *SnapshotsServiceOp) list(ctx context.Context, opt *ListOptions, listOpt *listSnapshotOptions) ([]Snapshot, *Response, error) {
|
||||
path := snapshotBasePath
|
||||
path, err := addOptions(path, opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
path, err = addOptions(path, listOpt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(snapshotsRoot)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Snapshots, resp, err
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
package godo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSnapshots_List(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/v2/snapshots", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, `{"snapshots":[{"id":"1"},{"id":"2", "size_gigabytes": 4.84}]}`)
|
||||
})
|
||||
ctx := context.Background()
|
||||
snapshots, _, err := client.Snapshots.List(ctx, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Snapshots.List returned error: %v", err)
|
||||
}
|
||||
|
||||
expected := []Snapshot{{ID: "1"}, {ID: "2", SizeGigaBytes: 4.84}}
|
||||
if !reflect.DeepEqual(snapshots, expected) {
|
||||
t.Errorf("Snapshots.List returned %+v, expected %+v", snapshots, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_ListVolume(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/v2/snapshots", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
expected := "volume"
|
||||
actual := r.URL.Query().Get("resource_type")
|
||||
if actual != expected {
|
||||
t.Errorf("'type' query = %v, expected %v", actual, expected)
|
||||
}
|
||||
fmt.Fprint(w, `{"snapshots":[{"id":"1"},{"id":"2"}]}`)
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
snapshots, _, err := client.Snapshots.ListVolume(ctx, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Snapshots.ListVolume returned error: %v", err)
|
||||
}
|
||||
|
||||
expected := []Snapshot{{ID: "1"}, {ID: "2"}}
|
||||
if !reflect.DeepEqual(snapshots, expected) {
|
||||
t.Errorf("Snapshots.ListVolume returned %+v, expected %+v", snapshots, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_ListDroplet(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/v2/snapshots", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
expected := "droplet"
|
||||
actual := r.URL.Query().Get("resource_type")
|
||||
if actual != expected {
|
||||
t.Errorf("'resource_type' query = %v, expected %v", actual, expected)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, `{"snapshots":[{"id":"1"},{"id":"2", "size_gigabytes": 4.84}]}`)
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
snapshots, _, err := client.Snapshots.ListDroplet(ctx, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Snapshots.ListDroplet returned error: %v", err)
|
||||
}
|
||||
|
||||
expected := []Snapshot{{ID: "1"}, {ID: "2", SizeGigaBytes: 4.84}}
|
||||
if !reflect.DeepEqual(snapshots, expected) {
|
||||
t.Errorf("Snapshots.ListDroplet returned %+v, expected %+v", snapshots, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_ListSnapshotsMultiplePages(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/v2/snapshots", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, `{"snapshots": [{"id":"1"},{"id":"2"}], "links":{"pages":{"next":"http://example.com/v2/snapshots/?page=2"}}}`)
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
_, _, err := client.Snapshots.List(ctx, &ListOptions{Page: 2})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_RetrievePageByNumber(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
jBlob := `
|
||||
{
|
||||
"snapshots": [{"id":"1"},{"id":"2"}],
|
||||
"links":{
|
||||
"pages":{
|
||||
"next":"http://example.com/v2/snapshots/?page=3",
|
||||
"prev":"http://example.com/v2/snapshots/?page=1",
|
||||
"last":"http://example.com/v2/snapshots/?page=3",
|
||||
"first":"http://example.com/v2/snapshots/?page=1"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
mux.HandleFunc("/v2/snapshots", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
opt := &ListOptions{Page: 2}
|
||||
_, _, err := client.Snapshots.List(ctx, opt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_GetSnapshotByID(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/v2/snapshots/12345", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, `{"snapshot":{"id":"12345"}}`)
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
snapshots, _, err := client.Snapshots.Get(ctx, "12345")
|
||||
if err != nil {
|
||||
t.Errorf("Snapshot.GetByID returned error: %v", err)
|
||||
}
|
||||
|
||||
expected := &Snapshot{ID: "12345"}
|
||||
if !reflect.DeepEqual(snapshots, expected) {
|
||||
t.Errorf("Snapshots.GetByID returned %+v, expected %+v", snapshots, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshots_Destroy(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/v2/snapshots/12345", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodDelete)
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
_, err := client.Snapshots.Delete(ctx, "12345")
|
||||
if err != nil {
|
||||
t.Errorf("Snapshot.Delete returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshot_String(t *testing.T) {
|
||||
snapshot := &Snapshot{
|
||||
ID: "1",
|
||||
Name: "Snapsh176ot",
|
||||
ResourceID: "0",
|
||||
ResourceType: "droplet",
|
||||
Regions: []string{"one"},
|
||||
MinDiskSize: 20,
|
||||
SizeGigaBytes: 4.84,
|
||||
Created: "2013-11-27T09:24:55Z",
|
||||
Tags: []string{"one", "two"},
|
||||
}
|
||||
|
||||
stringified := snapshot.String()
|
||||
expected := `godo.Snapshot{ID:"1", Name:"Snapsh176ot", ResourceID:"0", ResourceType:"droplet", Regions:["one"], MinDiskSize:20, SizeGigaBytes:4.84, Created:"2013-11-27T09:24:55Z", Tags:["one" "two"]}`
|
||||
if expected != stringified {
|
||||
t.Errorf("Snapshot.String returned %+v, expected %+v", stringified, expected)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,254 @@
|
|||
package godo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
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://docs.digitalocean.com/reference/api/api-reference/#tag/Block-Storage
|
||||
type StorageService interface {
|
||||
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)
|
||||
}
|
||||
|
||||
// StorageServiceOp handles communication with the storage volumes related methods of the
|
||||
// DigitalOcean API.
|
||||
type StorageServiceOp struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
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"`
|
||||
FilesystemType string `json:"filesystem_type"`
|
||||
FilesystemLabel string `json:"filesystem_label"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
func (f Volume) String() string {
|
||||
return Stringify(f)
|
||||
}
|
||||
|
||||
// URN returns the volume ID as a valid DO API URN
|
||||
func (f Volume) URN() string {
|
||||
return ToURN("Volume", f.ID)
|
||||
}
|
||||
|
||||
type storageVolumesRoot struct {
|
||||
Volumes []Volume `json:"volumes"`
|
||||
Meta *Meta `json:"meta"`
|
||||
}
|
||||
|
||||
type storageVolumeRoot struct {
|
||||
Volume *Volume `json:"volume"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
SnapshotID string `json:"snapshot_id"`
|
||||
FilesystemType string `json:"filesystem_type"`
|
||||
FilesystemLabel string `json:"filesystem_label"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// ListVolumes lists all storage volumes.
|
||||
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)
|
||||
} else if params.Region != "" {
|
||||
path = fmt.Sprintf("%s?region=%s", path, params.Region)
|
||||
} else if params.Name != "" {
|
||||
path = fmt.Sprintf("%s?name=%s", path, params.Name)
|
||||
}
|
||||
|
||||
if params.ListOptions != nil {
|
||||
var err error
|
||||
path, err = addOptions(path, params.ListOptions)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(storageVolumesRoot)
|
||||
resp, err := svc.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
if m := root.Meta; m != nil {
|
||||
resp.Meta = m
|
||||
}
|
||||
|
||||
return root.Volumes, resp, nil
|
||||
}
|
||||
|
||||
// CreateVolume creates a storage volume. The name must be unique.
|
||||
func (svc *StorageServiceOp) CreateVolume(ctx context.Context, createRequest *VolumeCreateRequest) (*Volume, *Response, error) {
|
||||
path := storageAllocPath
|
||||
|
||||
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, createRequest)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(storageVolumeRoot)
|
||||
resp, err := svc.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return root.Volume, resp, nil
|
||||
}
|
||||
|
||||
// GetVolume retrieves an individual storage volume.
|
||||
func (svc *StorageServiceOp) GetVolume(ctx context.Context, id string) (*Volume, *Response, error) {
|
||||
path := fmt.Sprintf("%s/%s", storageAllocPath, id)
|
||||
|
||||
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(storageVolumeRoot)
|
||||
resp, err := svc.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Volume, resp, nil
|
||||
}
|
||||
|
||||
// DeleteVolume deletes a storage volume.
|
||||
func (svc *StorageServiceOp) DeleteVolume(ctx context.Context, id string) (*Response, error) {
|
||||
path := fmt.Sprintf("%s/%s", storageAllocPath, id)
|
||||
|
||||
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.client.Do(ctx, req, nil)
|
||||
}
|
||||
|
||||
// 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"`
|
||||
Tags []string `json:"tags"`
|
||||
}
|
||||
|
||||
// ListSnapshots lists all snapshots related to a storage volume.
|
||||
func (svc *StorageServiceOp) ListSnapshots(ctx context.Context, volumeID string, opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||
path := fmt.Sprintf("%s/%s/snapshots", storageAllocPath, volumeID)
|
||||
path, err := addOptions(path, opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(snapshotsRoot)
|
||||
resp, err := svc.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
if m := root.Meta; m != nil {
|
||||
resp.Meta = m
|
||||
}
|
||||
|
||||
return root.Snapshots, resp, nil
|
||||
}
|
||||
|
||||
// CreateSnapshot creates a snapshot of a storage volume.
|
||||
func (svc *StorageServiceOp) CreateSnapshot(ctx context.Context, createRequest *SnapshotCreateRequest) (*Snapshot, *Response, error) {
|
||||
path := fmt.Sprintf("%s/%s/snapshots", storageAllocPath, createRequest.VolumeID)
|
||||
|
||||
req, err := svc.client.NewRequest(ctx, http.MethodPost, path, createRequest)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(snapshotRoot)
|
||||
resp, err := svc.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return root.Snapshot, resp, nil
|
||||
}
|
||||
|
||||
// GetSnapshot retrieves an individual snapshot.
|
||||
func (svc *StorageServiceOp) GetSnapshot(ctx context.Context, id string) (*Snapshot, *Response, error) {
|
||||
path := fmt.Sprintf("%s/%s", storageSnapPath, id)
|
||||
|
||||
req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(snapshotRoot)
|
||||
resp, err := svc.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Snapshot, resp, nil
|
||||
}
|
||||
|
||||
// DeleteSnapshot deletes a snapshot.
|
||||
func (svc *StorageServiceOp) DeleteSnapshot(ctx context.Context, id string) (*Response, error) {
|
||||
path := fmt.Sprintf("%s/%s", storageSnapPath, id)
|
||||
|
||||
req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return svc.client.Do(ctx, req, nil)
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package godo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// StorageActionsService is an interface for interfacing with the
|
||||
// storage actions endpoints of the Digital Ocean API.
|
||||
// See: https://docs.digitalocean.com/reference/api/api-reference/#tag/Block-Storage-Actions
|
||||
type StorageActionsService interface {
|
||||
Attach(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error)
|
||||
DetachByDropletID(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error)
|
||||
Get(ctx context.Context, volumeID string, actionID int) (*Action, *Response, error)
|
||||
List(ctx context.Context, volumeID string, opt *ListOptions) ([]Action, *Response, error)
|
||||
Resize(ctx context.Context, volumeID string, sizeGigabytes int, regionSlug string) (*Action, *Response, error)
|
||||
}
|
||||
|
||||
// StorageActionsServiceOp handles communication with the storage volumes
|
||||
// action related methods of the DigitalOcean API.
|
||||
type StorageActionsServiceOp struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
// StorageAttachment represents the attachment of a block storage
|
||||
// volume to a specific Droplet under the device name.
|
||||
type StorageAttachment struct {
|
||||
DropletID int `json:"droplet_id"`
|
||||
}
|
||||
|
||||
// Attach a storage volume to a Droplet.
|
||||
func (s *StorageActionsServiceOp) Attach(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error) {
|
||||
request := &ActionRequest{
|
||||
"type": "attach",
|
||||
"droplet_id": dropletID,
|
||||
}
|
||||
return s.doAction(ctx, volumeID, request)
|
||||
}
|
||||
|
||||
// DetachByDropletID a storage volume from a Droplet by Droplet ID.
|
||||
func (s *StorageActionsServiceOp) DetachByDropletID(ctx context.Context, volumeID string, dropletID int) (*Action, *Response, error) {
|
||||
request := &ActionRequest{
|
||||
"type": "detach",
|
||||
"droplet_id": dropletID,
|
||||
}
|
||||
return s.doAction(ctx, volumeID, request)
|
||||
}
|
||||
|
||||
// Get an action for a particular storage volume by id.
|
||||
func (s *StorageActionsServiceOp) Get(ctx context.Context, volumeID string, actionID int) (*Action, *Response, error) {
|
||||
path := fmt.Sprintf("%s/%d", storageAllocationActionPath(volumeID), actionID)
|
||||
return s.get(ctx, path)
|
||||
}
|
||||
|
||||
// List the actions for a particular storage volume.
|
||||
func (s *StorageActionsServiceOp) List(ctx context.Context, volumeID string, opt *ListOptions) ([]Action, *Response, error) {
|
||||
path := storageAllocationActionPath(volumeID)
|
||||
path, err := addOptions(path, opt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return s.list(ctx, path)
|
||||
}
|
||||
|
||||
// Resize a storage volume.
|
||||
func (s *StorageActionsServiceOp) Resize(ctx context.Context, volumeID string, sizeGigabytes int, regionSlug string) (*Action, *Response, error) {
|
||||
request := &ActionRequest{
|
||||
"type": "resize",
|
||||
"size_gigabytes": sizeGigabytes,
|
||||
"region": regionSlug,
|
||||
}
|
||||
return s.doAction(ctx, volumeID, request)
|
||||
}
|
||||
|
||||
func (s *StorageActionsServiceOp) doAction(ctx context.Context, volumeID string, request *ActionRequest) (*Action, *Response, error) {
|
||||
path := storageAllocationActionPath(volumeID)
|
||||
|
||||
req, err := s.client.NewRequest(ctx, http.MethodPost, path, request)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(actionRoot)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Event, resp, err
|
||||
}
|
||||
|
||||
func (s *StorageActionsServiceOp) get(ctx context.Context, path string) (*Action, *Response, error) {
|
||||
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(actionRoot)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Event, resp, err
|
||||
}
|
||||
|
||||
func (s *StorageActionsServiceOp) list(ctx context.Context, path string) ([]Action, *Response, error) {
|
||||
req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(actionsRoot)
|
||||
resp, err := s.client.Do(ctx, req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
if m := root.Meta; m != nil {
|
||||
resp.Meta = m
|
||||
}
|
||||
|
||||
return root.Actions, resp, err
|
||||
}
|
||||
|
||||
func storageAllocationActionPath(volumeID string) string {
|
||||
return fmt.Sprintf("%s/%s/actions", storageAllocPath, volumeID)
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package godo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStoragesActions_Attach(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
const (
|
||||
volumeID = "98d414c6-295e-4e3a-ac58-eb9456c1e1d1"
|
||||
dropletID = 12345
|
||||
)
|
||||
|
||||
attachRequest := &ActionRequest{
|
||||
"type": "attach",
|
||||
"droplet_id": float64(dropletID), // encoding/json decodes numbers as floats
|
||||
}
|
||||
|
||||
mux.HandleFunc("/v2/volumes/"+volumeID+"/actions", func(w http.ResponseWriter, r *http.Request) {
|
||||
v := new(ActionRequest)
|
||||
err := json.NewDecoder(r.Body).Decode(v)
|
||||
if err != nil {
|
||||
t.Fatalf("decode json: %v", err)
|
||||
}
|
||||
|
||||
testMethod(t, r, http.MethodPost)
|
||||
if !reflect.DeepEqual(v, attachRequest) {
|
||||
t.Errorf("want=%#v", attachRequest)
|
||||
t.Errorf("got=%#v", v)
|
||||
}
|
||||
fmt.Fprintf(w, `{"action":{"status":"in-progress"}}`)
|
||||
})
|
||||
|
||||
_, _, err := client.StorageActions.Attach(ctx, volumeID, dropletID)
|
||||
if err != nil {
|
||||
t.Errorf("StoragesActions.Attach returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoragesActions_DetachByDropletID(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
volumeID := "98d414c6-295e-4e3a-ac58-eb9456c1e1d1"
|
||||
dropletID := 123456
|
||||
|
||||
detachByDropletIDRequest := &ActionRequest{
|
||||
"type": "detach",
|
||||
"droplet_id": float64(dropletID), // encoding/json decodes numbers as floats
|
||||
}
|
||||
|
||||
mux.HandleFunc("/v2/volumes/"+volumeID+"/actions", func(w http.ResponseWriter, r *http.Request) {
|
||||
v := new(ActionRequest)
|
||||
err := json.NewDecoder(r.Body).Decode(v)
|
||||
if err != nil {
|
||||
t.Fatalf("decode json: %v", err)
|
||||
}
|
||||
|
||||
testMethod(t, r, http.MethodPost)
|
||||
if !reflect.DeepEqual(v, detachByDropletIDRequest) {
|
||||
t.Errorf("want=%#v", detachByDropletIDRequest)
|
||||
t.Errorf("got=%#v", v)
|
||||
}
|
||||
fmt.Fprintf(w, `{"action":{"status":"in-progress"}}`)
|
||||
})
|
||||
|
||||
_, _, err := client.StorageActions.DetachByDropletID(ctx, volumeID, dropletID)
|
||||
if err != nil {
|
||||
t.Errorf("StoragesActions.DetachByDropletID returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageActions_Get(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
volumeID := "98d414c6-295e-4e3a-ac58-eb9456c1e1d1"
|
||||
|
||||
mux.HandleFunc("/v2/volumes/"+volumeID+"/actions/456", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprintf(w, `{"action":{"status":"in-progress"}}`)
|
||||
})
|
||||
|
||||
action, _, err := client.StorageActions.Get(ctx, volumeID, 456)
|
||||
if err != nil {
|
||||
t.Errorf("StorageActions.Get returned error: %v", err)
|
||||
}
|
||||
|
||||
expected := &Action{Status: "in-progress"}
|
||||
if !reflect.DeepEqual(action, expected) {
|
||||
t.Errorf("StorageActions.Get returned %+v, expected %+v", action, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageActions_List(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
volumeID := "98d414c6-295e-4e3a-ac58-eb9456c1e1d1"
|
||||
|
||||
mux.HandleFunc("/v2/volumes/"+volumeID+"/actions", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprintf(w, `{
|
||||
"actions": [
|
||||
{
|
||||
"status": "in-progress"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
"total": 1
|
||||
}
|
||||
}`)
|
||||
})
|
||||
|
||||
actions, resp, err := client.StorageActions.List(ctx, volumeID, nil)
|
||||
if err != nil {
|
||||
t.Errorf("StorageActions.List returned error: %v", err)
|
||||
}
|
||||
|
||||
expectedActions := []Action{{Status: "in-progress"}}
|
||||
if !reflect.DeepEqual(actions, expectedActions) {
|
||||
t.Errorf("StorageActions.List returned actions %+v, expected %+v", actions, expectedActions)
|
||||
}
|
||||
|
||||
expectedMeta := &Meta{Total: 1}
|
||||
if !reflect.DeepEqual(resp.Meta, expectedMeta) {
|
||||
t.Errorf("StorageActions.List returned meta %+v, expected %+v", resp.Meta, expectedMeta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoragesActions_Resize(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
volumeID := "98d414c6-295e-4e3a-ac58-eb9456c1e1d1"
|
||||
|
||||
resizeRequest := &ActionRequest{
|
||||
"type": "resize",
|
||||
"size_gigabytes": float64(500),
|
||||
"region": "nyc1",
|
||||
}
|
||||
|
||||
mux.HandleFunc("/v2/volumes/"+volumeID+"/actions", func(w http.ResponseWriter, r *http.Request) {
|
||||
v := new(ActionRequest)
|
||||
err := json.NewDecoder(r.Body).Decode(v)
|
||||
if err != nil {
|
||||
t.Fatalf("decode json: %v", err)
|
||||
}
|
||||
|
||||
testMethod(t, r, http.MethodPost)
|
||||
if !reflect.DeepEqual(v, resizeRequest) {
|
||||
t.Errorf("want=%#v", resizeRequest)
|
||||
t.Errorf("got=%#v", v)
|
||||
}
|
||||
fmt.Fprintf(w, `{"action":{"status":"in-progress"}}`)
|
||||
})
|
||||
|
||||
_, _, err := client.StorageActions.Resize(ctx, volumeID, 500, "nyc1")
|
||||
if err != nil {
|
||||
t.Errorf("StoragesActions.Resize returned error: %v", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,719 @@
|
|||
package godo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestStorageVolumes_ListStorageVolumes(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
jBlob := `
|
||||
{
|
||||
"volumes": [
|
||||
{
|
||||
"user_id": 42,
|
||||
"region": {"slug": "nyc3"},
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my volume",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"droplet_ids": [10],
|
||||
"created_at": "2002-10-02T15:00:00.05Z",
|
||||
"filesystem_type": "",
|
||||
"filesystem_label": "",
|
||||
"tags": ["tag1", "tag2"]
|
||||
},
|
||||
{
|
||||
"user_id": 42,
|
||||
"region": {"slug": "nyc3"},
|
||||
"id": "96d414c6-295e-4e3a-ac59-eb9456c1e1d1",
|
||||
"name": "my other volume",
|
||||
"description": "my other description",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2012-10-03T15:00:01.05Z",
|
||||
"filesystem_type": "ext4",
|
||||
"filesystem_label": "my-volume",
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"pages": {
|
||||
"last": "https://api.digitalocean.com/v2/volumes?page=2",
|
||||
"next": "https://api.digitalocean.com/v2/volumes?page=2"
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"total": 28
|
||||
}
|
||||
}`
|
||||
|
||||
mux.HandleFunc("/v2/volumes/", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
volumes, resp, err := client.Storage.ListVolumes(ctx, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Storage.ListVolumes returned error: %v", err)
|
||||
}
|
||||
|
||||
expectedVolume := []Volume{
|
||||
{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my volume",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
DropletIDs: []int{10},
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
},
|
||||
{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
ID: "96d414c6-295e-4e3a-ac59-eb9456c1e1d1",
|
||||
Name: "my other volume",
|
||||
Description: "my other description",
|
||||
SizeGigaBytes: 100,
|
||||
CreatedAt: time.Date(2012, 10, 03, 15, 00, 01, 50000000, time.UTC),
|
||||
FilesystemType: "ext4",
|
||||
FilesystemLabel: "my-volume",
|
||||
Tags: []string{},
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(volumes, expectedVolume) {
|
||||
t.Errorf("Storage.ListVolumes returned volumes %+v, expected %+v", volumes, expectedVolume)
|
||||
}
|
||||
|
||||
expectedMeta := &Meta{
|
||||
Total: 28,
|
||||
}
|
||||
if !reflect.DeepEqual(resp.Meta, expectedMeta) {
|
||||
t.Errorf("Storage.ListVolumes returned meta %+v, expected %+v", resp.Meta, expectedMeta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageVolumes_Get(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
want := &Volume{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my volume",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
FilesystemType: "xfs",
|
||||
FilesystemLabel: "my-vol",
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
}
|
||||
jBlob := `{
|
||||
"volume":{
|
||||
"region": {"slug":"nyc3"},
|
||||
"attached_to_droplet": null,
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my volume",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2002-10-02T15:00:00.05Z",
|
||||
"filesystem_type": "xfs",
|
||||
"filesystem_label": "my-vol",
|
||||
"tags": ["tag1", "tag2"]
|
||||
}
|
||||
}`
|
||||
|
||||
mux.HandleFunc("/v2/volumes/80d414c6-295e-4e3a-ac58-eb9456c1e1d1", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
got, _, err := client.Storage.GetVolume(ctx, "80d414c6-295e-4e3a-ac58-eb9456c1e1d1")
|
||||
if err != nil {
|
||||
t.Errorf("Storage.GetVolume returned error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Storage.GetVolume returned %+v, want %+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageVolumes_ListVolumesByName(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
jBlob :=
|
||||
`{
|
||||
"volumes": [
|
||||
{
|
||||
"region": {"slug": "nyc3"},
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "myvolume",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"droplet_ids": [10],
|
||||
"created_at": "2002-10-02T15:00:00.05Z",
|
||||
"filesystem_type": "",
|
||||
"filesystem_label": "",
|
||||
"tags": ["tag1", "tag2"]
|
||||
}
|
||||
],
|
||||
"links": {},
|
||||
"meta": {
|
||||
"total": 1
|
||||
}
|
||||
}`
|
||||
|
||||
expectedVolumes := []Volume{
|
||||
{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "myvolume",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
DropletIDs: []int{10},
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
},
|
||||
}
|
||||
|
||||
mux.HandleFunc("/v2/volumes", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("name") != "myvolume" || r.URL.Query().Get("region") != "" {
|
||||
t.Errorf("Storage.ListVolumeByName did not request the correct name or region")
|
||||
}
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
options := &ListVolumeParams{
|
||||
Name: "myvolume",
|
||||
}
|
||||
volumes, resp, err := client.Storage.ListVolumes(ctx, options)
|
||||
if err != nil {
|
||||
t.Errorf("Storage.ListVolumeByName returned error: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(volumes, expectedVolumes) {
|
||||
t.Errorf("Storage.ListVolumeByName returned volumes %+v, expected %+v", volumes, expectedVolumes)
|
||||
}
|
||||
|
||||
expectedMeta := &Meta{
|
||||
Total: 1,
|
||||
}
|
||||
if !reflect.DeepEqual(resp.Meta, expectedMeta) {
|
||||
t.Errorf("Storage.ListVolumeByName returned meta %+v, expected %+v", resp.Meta, expectedMeta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageVolumes_ListVolumesByRegion(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
jBlob :=
|
||||
`{
|
||||
"volumes": [
|
||||
{
|
||||
"region": {"slug": "nyc3"},
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "myvolume",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"droplet_ids": [10],
|
||||
"created_at": "2002-10-02T15:00:00.05Z",
|
||||
"filesystem_type": "",
|
||||
"filesystem_label": "",
|
||||
"tags": ["tag1", "tag2"]
|
||||
}
|
||||
],
|
||||
"links": {},
|
||||
"meta": {
|
||||
"total": 1
|
||||
}
|
||||
}`
|
||||
|
||||
expectedVolumes := []Volume{
|
||||
{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "myvolume",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
DropletIDs: []int{10},
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
},
|
||||
}
|
||||
|
||||
mux.HandleFunc("/v2/volumes", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("region") != "nyc3" || r.URL.Query().Get("name") != "" {
|
||||
t.Errorf("Storage.ListVolumeByName did not request the correct name or region")
|
||||
}
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
options := &ListVolumeParams{
|
||||
Region: "nyc3",
|
||||
}
|
||||
volumes, resp, err := client.Storage.ListVolumes(ctx, options)
|
||||
if err != nil {
|
||||
t.Errorf("Storage.ListVolumeByName returned error: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(volumes, expectedVolumes) {
|
||||
t.Errorf("Storage.ListVolumeByName returned volumes %+v, expected %+v", volumes, expectedVolumes)
|
||||
}
|
||||
|
||||
expectedMeta := &Meta{
|
||||
Total: 1,
|
||||
}
|
||||
if !reflect.DeepEqual(resp.Meta, expectedMeta) {
|
||||
t.Errorf("Storage.ListVolumeByName returned meta %+v, expected %+v", resp.Meta, expectedMeta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageVolumes_ListVolumesByNameAndRegion(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
jBlob :=
|
||||
`{
|
||||
"volumes": [
|
||||
{
|
||||
"region": {"slug": "nyc3"},
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "myvolume",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"droplet_ids": [10],
|
||||
"created_at": "2002-10-02T15:00:00.05Z",
|
||||
"filesystem_type": "",
|
||||
"filesystem_label": "",
|
||||
"tags": ["tag1", "tag2"]
|
||||
}
|
||||
],
|
||||
"links": {},
|
||||
"meta": {
|
||||
"total": 1
|
||||
}
|
||||
}`
|
||||
|
||||
expectedVolumes := []Volume{
|
||||
{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "myvolume",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
DropletIDs: []int{10},
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
},
|
||||
}
|
||||
|
||||
mux.HandleFunc("/v2/volumes", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("region") != "nyc3" || r.URL.Query().Get("name") != "myvolume" {
|
||||
t.Errorf("Storage.ListVolumeByName did not request the correct name or region")
|
||||
}
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
options := &ListVolumeParams{
|
||||
Region: "nyc3",
|
||||
Name: "myvolume",
|
||||
}
|
||||
volumes, resp, err := client.Storage.ListVolumes(ctx, options)
|
||||
if err != nil {
|
||||
t.Errorf("Storage.ListVolumeByName returned error: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(volumes, expectedVolumes) {
|
||||
t.Errorf("Storage.ListVolumeByName returned volumes %+v, expected %+v", volumes, expectedVolumes)
|
||||
}
|
||||
|
||||
expectedMeta := &Meta{
|
||||
Total: 1,
|
||||
}
|
||||
if !reflect.DeepEqual(resp.Meta, expectedMeta) {
|
||||
t.Errorf("Storage.ListVolumeByName returned meta %+v, expected %+v", resp.Meta, expectedMeta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageVolumes_Create(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
createRequest := &VolumeCreateRequest{
|
||||
Region: "nyc3",
|
||||
Name: "my volume",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
}
|
||||
|
||||
want := &Volume{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my volume",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
}
|
||||
jBlob := `{
|
||||
"volume":{
|
||||
"region": {"slug":"nyc3"},
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my volume",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2002-10-02T15:00:00.05Z",
|
||||
"tags": ["tag1", "tag2"]
|
||||
},
|
||||
"links": {}
|
||||
}`
|
||||
|
||||
mux.HandleFunc("/v2/volumes", func(w http.ResponseWriter, r *http.Request) {
|
||||
v := new(VolumeCreateRequest)
|
||||
err := json.NewDecoder(r.Body).Decode(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testMethod(t, r, http.MethodPost)
|
||||
if !reflect.DeepEqual(v, createRequest) {
|
||||
t.Errorf("Request body = %+v, expected %+v", v, createRequest)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
got, _, err := client.Storage.CreateVolume(ctx, createRequest)
|
||||
if err != nil {
|
||||
t.Errorf("Storage.CreateVolume returned error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Storage.CreateVolume returned %+v, want %+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageVolumes_CreateFormatted(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
createRequest := &VolumeCreateRequest{
|
||||
Region: "nyc3",
|
||||
Name: "my volume",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
FilesystemType: "xfs",
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
}
|
||||
|
||||
want := &Volume{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my volume",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
FilesystemType: "xfs",
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
}
|
||||
jBlob := `{
|
||||
"volume":{
|
||||
"region": {"slug":"nyc3"},
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my volume",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2002-10-02T15:00:00.05Z",
|
||||
"filesystem_type": "xfs",
|
||||
"filesystem_label": "",
|
||||
"tags": ["tag1", "tag2"]
|
||||
},
|
||||
"links": {}
|
||||
}`
|
||||
|
||||
mux.HandleFunc("/v2/volumes", func(w http.ResponseWriter, r *http.Request) {
|
||||
v := new(VolumeCreateRequest)
|
||||
err := json.NewDecoder(r.Body).Decode(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testMethod(t, r, http.MethodPost)
|
||||
if !reflect.DeepEqual(v, createRequest) {
|
||||
t.Errorf("Request body = %+v, expected %+v", v, createRequest)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
got, _, err := client.Storage.CreateVolume(ctx, createRequest)
|
||||
if err != nil {
|
||||
t.Errorf("Storage.CreateVolume returned error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Storage.CreateVolume returned %+v, want %+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageVolumes_CreateFromSnapshot(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
createRequest := &VolumeCreateRequest{
|
||||
Name: "my-volume-from-a-snapshot",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
SnapshotID: "0d165eff-0b4c-11e7-9093-0242ac110207",
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
}
|
||||
|
||||
want := &Volume{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my-volume-from-a-snapshot",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
}
|
||||
jBlob := `{
|
||||
"volume":{
|
||||
"region": {"slug":"nyc3"},
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my-volume-from-a-snapshot",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2002-10-02T15:00:00.05Z",
|
||||
"tags": ["tag1", "tag2"]
|
||||
},
|
||||
"links": {}
|
||||
}`
|
||||
|
||||
mux.HandleFunc("/v2/volumes", func(w http.ResponseWriter, r *http.Request) {
|
||||
v := new(VolumeCreateRequest)
|
||||
err := json.NewDecoder(r.Body).Decode(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testMethod(t, r, http.MethodPost)
|
||||
if !reflect.DeepEqual(v, createRequest) {
|
||||
t.Errorf("Request body = %+v, expected %+v", v, createRequest)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
got, _, err := client.Storage.CreateVolume(ctx, createRequest)
|
||||
if err != nil {
|
||||
t.Errorf("Storage.CreateVolume returned error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Storage.CreateVolume returned %+v, want %+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageVolumes_Destroy(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/v2/volumes/80d414c6-295e-4e3a-ac58-eb9456c1e1d1", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodDelete)
|
||||
})
|
||||
|
||||
_, err := client.Storage.DeleteVolume(ctx, "80d414c6-295e-4e3a-ac58-eb9456c1e1d1")
|
||||
if err != nil {
|
||||
t.Errorf("Storage.DeleteVolume returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageSnapshots_ListStorageSnapshots(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
jBlob := `
|
||||
{
|
||||
"snapshots": [
|
||||
{
|
||||
"regions": ["nyc3"],
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my snapshot",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2002-10-02T15:00:00.05Z"
|
||||
},
|
||||
{
|
||||
"regions": ["nyc3"],
|
||||
"id": "96d414c6-295e-4e3a-ac59-eb9456c1e1d1",
|
||||
"name": "my other snapshot",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2012-10-03T15:00:01.05Z"
|
||||
}
|
||||
],
|
||||
"links": {
|
||||
"pages": {
|
||||
"last": "https://api.digitalocean.com/v2/volumes?page=2",
|
||||
"next": "https://api.digitalocean.com/v2/volumes?page=2"
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"total": 28
|
||||
}
|
||||
}`
|
||||
|
||||
mux.HandleFunc("/v2/volumes/98d414c6-295e-4e3a-ac58-eb9456c1e1d1/snapshots", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
volumes, resp, err := client.Storage.ListSnapshots(ctx, "98d414c6-295e-4e3a-ac58-eb9456c1e1d1", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Storage.ListSnapshots returned error: %v", err)
|
||||
}
|
||||
|
||||
expectedSnapshots := []Snapshot{
|
||||
{
|
||||
Regions: []string{"nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my snapshot",
|
||||
SizeGigaBytes: 100,
|
||||
Created: "2002-10-02T15:00:00.05Z",
|
||||
},
|
||||
{
|
||||
Regions: []string{"nyc3"},
|
||||
ID: "96d414c6-295e-4e3a-ac59-eb9456c1e1d1",
|
||||
Name: "my other snapshot",
|
||||
SizeGigaBytes: 100,
|
||||
Created: "2012-10-03T15:00:01.05Z",
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(volumes, expectedSnapshots) {
|
||||
t.Errorf("Storage.ListSnapshots returned snapshots %+v, expected %+v", volumes, expectedSnapshots)
|
||||
}
|
||||
|
||||
expectedMeta := &Meta{
|
||||
Total: 28,
|
||||
}
|
||||
if !reflect.DeepEqual(resp.Meta, expectedMeta) {
|
||||
t.Errorf("Storage.ListSnapshots returned meta %+v, expected %+v", resp.Meta, expectedMeta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageSnapshots_Get(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
want := &Snapshot{
|
||||
Regions: []string{"nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my snapshot",
|
||||
SizeGigaBytes: 100,
|
||||
Created: "2002-10-02T15:00:00.05Z",
|
||||
}
|
||||
jBlob := `{
|
||||
"snapshot":{
|
||||
"regions": ["nyc3"],
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my snapshot",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2002-10-02T15:00:00.05Z"
|
||||
}
|
||||
}`
|
||||
|
||||
mux.HandleFunc("/v2/snapshots/80d414c6-295e-4e3a-ac58-eb9456c1e1d1", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodGet)
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
got, _, err := client.Storage.GetSnapshot(ctx, "80d414c6-295e-4e3a-ac58-eb9456c1e1d1")
|
||||
if err != nil {
|
||||
t.Errorf("Storage.GetSnapshot returned error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Storage.GetSnapshot returned %+v, want %+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageSnapshots_Create(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
createRequest := &SnapshotCreateRequest{
|
||||
VolumeID: "98d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my snapshot",
|
||||
Description: "my description",
|
||||
Tags: []string{"one", "two"},
|
||||
}
|
||||
|
||||
want := &Snapshot{
|
||||
Regions: []string{"nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my snapshot",
|
||||
SizeGigaBytes: 100,
|
||||
Created: "2002-10-02T15:00:00.05Z",
|
||||
Tags: []string{"one", "two"},
|
||||
}
|
||||
jBlob := `{
|
||||
"snapshot":{
|
||||
"regions": ["nyc3"],
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my snapshot",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2002-10-02T15:00:00.05Z",
|
||||
"tags": ["one", "two"]
|
||||
},
|
||||
"links": {
|
||||
"pages": {
|
||||
"last": "https://api.digitalocean.com/v2/volumes/98d414c6-295e-4e3a-ac58-eb9456c1e1d1/snapshots?page=2",
|
||||
"next": "https://api.digitalocean.com/v2/volumes/98d414c6-295e-4e3a-ac58-eb9456c1e1d1/snapshots?page=2"
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"total": 28
|
||||
}
|
||||
}`
|
||||
|
||||
mux.HandleFunc("/v2/volumes/98d414c6-295e-4e3a-ac58-eb9456c1e1d1/snapshots", func(w http.ResponseWriter, r *http.Request) {
|
||||
v := new(SnapshotCreateRequest)
|
||||
err := json.NewDecoder(r.Body).Decode(v)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testMethod(t, r, http.MethodPost)
|
||||
if !reflect.DeepEqual(v, createRequest) {
|
||||
t.Errorf("Request body = %+v, expected %+v", v, createRequest)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
got, _, err := client.Storage.CreateSnapshot(ctx, createRequest)
|
||||
if err != nil {
|
||||
t.Errorf("Storage.CreateSnapshot returned error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Storage.CreateSnapshot returned %+v, want %+v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageSnapshots_Destroy(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/v2/snapshots/80d414c6-295e-4e3a-ac58-eb9456c1e1d1", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, http.MethodDelete)
|
||||
})
|
||||
|
||||
_, err := client.Storage.DeleteSnapshot(ctx, "80d414c6-295e-4e3a-ac58-eb9456c1e1d1")
|
||||
if err != nil {
|
||||
t.Errorf("Storage.DeleteSnapshot returned error: %v", err)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue