Merge pull request #108 from digitalocean/feature/snapshots
Add snapshots endpoints.
This commit is contained in:
commit
bec8984b6b
|
@ -131,7 +131,7 @@ type kernelsRoot struct {
|
|||
Links *Links `json:"links"`
|
||||
}
|
||||
|
||||
type snapshotsRoot struct {
|
||||
type dropletSnapshotsRoot struct {
|
||||
Snapshots []Image `json:"snapshots,omitempty"`
|
||||
Links *Links `json:"links"`
|
||||
}
|
||||
|
@ -509,7 +509,7 @@ func (s *DropletsServiceOp) Snapshots(dropletID int, opt *ListOptions) ([]Image,
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(snapshotsRoot)
|
||||
root := new(dropletSnapshotsRoot)
|
||||
resp, err := s.client.Do(req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
|
|
6
godo.go
6
godo.go
|
@ -55,6 +55,7 @@ type Client struct {
|
|||
Sizes SizesService
|
||||
FloatingIPs FloatingIPsService
|
||||
FloatingIPActions FloatingIPActionsService
|
||||
Snapshots SnapshotsService
|
||||
Storage StorageService
|
||||
StorageActions StorageActionsService
|
||||
Tags TagsService
|
||||
|
@ -155,13 +156,14 @@ func NewClient(httpClient *http.Client) *Client {
|
|||
c.Domains = &DomainsServiceOp{client: c}
|
||||
c.Droplets = &DropletsServiceOp{client: c}
|
||||
c.DropletActions = &DropletActionsServiceOp{client: c}
|
||||
c.FloatingIPs = &FloatingIPsServiceOp{client: c}
|
||||
c.FloatingIPActions = &FloatingIPActionsServiceOp{client: c}
|
||||
c.Images = &ImagesServiceOp{client: c}
|
||||
c.ImageActions = &ImageActionsServiceOp{client: c}
|
||||
c.Keys = &KeysServiceOp{client: c}
|
||||
c.Regions = &RegionsServiceOp{client: c}
|
||||
c.Snapshots = &SnapshotsServiceOp{client: c}
|
||||
c.Sizes = &SizesServiceOp{client: c}
|
||||
c.FloatingIPs = &FloatingIPsServiceOp{client: c}
|
||||
c.FloatingIPActions = &FloatingIPActionsServiceOp{client: c}
|
||||
c.Storage = &StorageServiceOp{client: c}
|
||||
c.StorageActions = &StorageActionsServiceOp{client: c}
|
||||
c.Tags = &TagsServiceOp{client: c}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
package godo
|
||||
|
||||
import "fmt"
|
||||
|
||||
const snapshotBasePath = "v2/snapshots"
|
||||
|
||||
// SnapshotsService is an interface for interfacing with the snapshots
|
||||
// endpoints of the DigitalOcean API
|
||||
// See: https://developers.digitalocean.com/documentation/v2#snapshots
|
||||
type SnapshotsService interface {
|
||||
List(*ListOptions) ([]Snapshot, *Response, error)
|
||||
ListVolume(*ListOptions) ([]Snapshot, *Response, error)
|
||||
ListDroplet(*ListOptions) ([]Snapshot, *Response, error)
|
||||
Get(string) (*Snapshot, *Response, error)
|
||||
Delete(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 int `json:"size_gigabytes,omitempty"`
|
||||
Created string `json:"created_at,omitempty"`
|
||||
}
|
||||
|
||||
type snapshotRoot struct {
|
||||
Snapshot *Snapshot `json:"snapshot"`
|
||||
}
|
||||
|
||||
type snapshotsRoot struct {
|
||||
Snapshots []Snapshot `json:"snapshots"`
|
||||
Links *Links `json:"links,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(opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||
return s.list(opt, nil)
|
||||
}
|
||||
|
||||
// ListDroplet lists all the Droplet snapshots.
|
||||
func (s *SnapshotsServiceOp) ListDroplet(opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||
listOpt := listSnapshotOptions{ResourceType: "droplet"}
|
||||
return s.list(opt, &listOpt)
|
||||
}
|
||||
|
||||
// ListVolume lists all the volume snapshots.
|
||||
func (s *SnapshotsServiceOp) ListVolume(opt *ListOptions) ([]Snapshot, *Response, error) {
|
||||
listOpt := listSnapshotOptions{ResourceType: "volume"}
|
||||
return s.list(opt, &listOpt)
|
||||
}
|
||||
|
||||
// GetByID retrieves an snapshot by id.
|
||||
func (s *SnapshotsServiceOp) Get(snapshotID string) (*Snapshot, *Response, error) {
|
||||
return s.get(interface{}(snapshotID))
|
||||
}
|
||||
|
||||
// Delete an snapshot.
|
||||
func (s *SnapshotsServiceOp) Delete(snapshotID string) (*Response, error) {
|
||||
path := fmt.Sprintf("%s/%s", snapshotBasePath, snapshotID)
|
||||
|
||||
req, err := s.client.NewRequest("DELETE", path, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(req, nil)
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// Helper method for getting an individual snapshot
|
||||
func (s *SnapshotsServiceOp) get(ID interface{}) (*Snapshot, *Response, error) {
|
||||
path := fmt.Sprintf("%s/%v", snapshotBasePath, ID)
|
||||
|
||||
req, err := s.client.NewRequest("GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(snapshotRoot)
|
||||
resp, err := s.client.Do(req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return root.Snapshot, resp, err
|
||||
}
|
||||
|
||||
// Helper method for listing snapshots
|
||||
func (s *SnapshotsServiceOp) list(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("GET", path, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(snapshotsRoot)
|
||||
resp, err := s.client.Do(req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
if l := root.Links; l != nil {
|
||||
resp.Links = l
|
||||
}
|
||||
|
||||
return root.Snapshots, resp, err
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
package godo
|
||||
|
||||
import (
|
||||
"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, "GET")
|
||||
fmt.Fprint(w, `{"snapshots":[{"id":"1"},{"id":"2"}]}`)
|
||||
})
|
||||
|
||||
snapshots, _, err := client.Snapshots.List(nil)
|
||||
if err != nil {
|
||||
t.Errorf("Snapshots.List returned error: %v", err)
|
||||
}
|
||||
|
||||
expected := []Snapshot{{ID: "1"}, {ID: "2"}}
|
||||
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, "GET")
|
||||
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"}]}`)
|
||||
})
|
||||
|
||||
snapshots, _, err := client.Snapshots.ListVolume(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, "GET")
|
||||
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"}]}`)
|
||||
})
|
||||
|
||||
snapshots, _, err := client.Snapshots.ListDroplet(nil)
|
||||
if err != nil {
|
||||
t.Errorf("Snapshots.ListDroplet returned error: %v", err)
|
||||
}
|
||||
|
||||
expected := []Snapshot{{ID: "1"}, {ID: "2"}}
|
||||
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, "GET")
|
||||
fmt.Fprint(w, `{"snapshots": [{"id":"1"},{"id":"2"}], "links":{"pages":{"next":"http://example.com/v2/snapshots/?page=2"}}}`)
|
||||
})
|
||||
|
||||
_, resp, err := client.Snapshots.List(&ListOptions{Page: 2})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkCurrentPage(t, resp, 1)
|
||||
}
|
||||
|
||||
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, "GET")
|
||||
fmt.Fprint(w, jBlob)
|
||||
})
|
||||
|
||||
opt := &ListOptions{Page: 2}
|
||||
_, resp, err := client.Snapshots.List(opt)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
checkCurrentPage(t, resp, 2)
|
||||
}
|
||||
|
||||
func TestSnapshots_GetSnapshotByID(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
|
||||
mux.HandleFunc("/v2/snapshots/12345", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "GET")
|
||||
fmt.Fprint(w, `{"snapshot":{"id":"12345"}}`)
|
||||
})
|
||||
|
||||
snapshots, _, err := client.Snapshots.Get("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, "DELETE")
|
||||
})
|
||||
|
||||
_, err := client.Snapshots.Delete("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: 0,
|
||||
Created: "2013-11-27T09:24:55Z",
|
||||
}
|
||||
|
||||
stringified := snapshot.String()
|
||||
expected := `godo.Snapshot{ID:"1", Name:"Snapsh176ot", ResourceID:"0", ResourceType:"droplet", Regions:["one"], MinDiskSize:20, SizeGigaBytes:0, Created:"2013-11-27T09:24:55Z"}`
|
||||
if expected != stringified {
|
||||
t.Errorf("Snapshot.String returned %+v, expected %+v", stringified, expected)
|
||||
}
|
||||
}
|
27
storage.go
27
storage.go
|
@ -150,27 +150,6 @@ func (svc *StorageServiceOp) DeleteVolume(id string) (*Response, error) {
|
|||
return svc.client.Do(req, nil)
|
||||
}
|
||||
|
||||
// Snapshot represents a Digital Ocean block store snapshot.
|
||||
type Snapshot struct {
|
||||
ID string `json:"id"`
|
||||
VolumeID string `json:"volume_id"`
|
||||
Region *Region `json:"region"`
|
||||
Name string `json:"name"`
|
||||
SizeGigaBytes int64 `json:"size_gigabytes"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
type storageSnapsRoot struct {
|
||||
Snapshots []Snapshot `json:"snapshots"`
|
||||
Links *Links `json:"links"`
|
||||
}
|
||||
|
||||
type storageSnapRoot struct {
|
||||
Snapshot *Snapshot `json:"snapshot"`
|
||||
Links *Links `json:"links,omitempty"`
|
||||
}
|
||||
|
||||
// SnapshotCreateRequest represents a request to create a block store
|
||||
// volume.
|
||||
type SnapshotCreateRequest struct {
|
||||
|
@ -192,7 +171,7 @@ func (svc *StorageServiceOp) ListSnapshots(volumeID string, opt *ListOptions) ([
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(storageSnapsRoot)
|
||||
root := new(snapshotsRoot)
|
||||
resp, err := svc.client.Do(req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
|
@ -214,7 +193,7 @@ func (svc *StorageServiceOp) CreateSnapshot(createRequest *SnapshotCreateRequest
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(storageSnapRoot)
|
||||
root := new(snapshotRoot)
|
||||
resp, err := svc.client.Do(req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
|
@ -231,7 +210,7 @@ func (svc *StorageServiceOp) GetSnapshot(id string) (*Snapshot, *Response, error
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
root := new(storageSnapRoot)
|
||||
root := new(snapshotRoot)
|
||||
resp, err := svc.client.Do(req, root)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
|
|
|
@ -204,20 +204,16 @@ func TestStorageSnapshots_ListStorageSnapshots(t *testing.T) {
|
|||
{
|
||||
"snapshots": [
|
||||
{
|
||||
"region": {"slug": "nyc3"},
|
||||
"regions": ["nyc3"],
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"volume_id": "98d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my snapshot",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2002-10-02T15:00:00.05Z"
|
||||
},
|
||||
{
|
||||
"region": {"slug": "nyc3"},
|
||||
"regions": ["nyc3"],
|
||||
"id": "96d414c6-295e-4e3a-ac59-eb9456c1e1d1",
|
||||
"volume_id": "98d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my other snapshot",
|
||||
"description": "my other description",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2012-10-03T15:00:01.05Z"
|
||||
}
|
||||
|
@ -245,22 +241,18 @@ func TestStorageSnapshots_ListStorageSnapshots(t *testing.T) {
|
|||
|
||||
expected := []Snapshot{
|
||||
{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
Regions: []string{"nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
VolumeID: "98d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my snapshot",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
Created: "2002-10-02T15:00:00.05Z",
|
||||
},
|
||||
{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
Regions: []string{"nyc3"},
|
||||
ID: "96d414c6-295e-4e3a-ac59-eb9456c1e1d1",
|
||||
VolumeID: "98d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my other snapshot",
|
||||
Description: "my other description",
|
||||
SizeGigaBytes: 100,
|
||||
CreatedAt: time.Date(2012, 10, 03, 15, 00, 01, 50000000, time.UTC),
|
||||
Created: "2012-10-03T15:00:01.05Z",
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(volumes, expected) {
|
||||
|
@ -272,21 +264,17 @@ func TestStorageSnapshots_Get(t *testing.T) {
|
|||
setup()
|
||||
defer teardown()
|
||||
want := &Snapshot{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
Regions: []string{"nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
VolumeID: "98d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my snapshot",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
Created: "2002-10-02T15:00:00.05Z",
|
||||
}
|
||||
jBlob := `{
|
||||
"snapshot":{
|
||||
"region": {"slug": "nyc3"},
|
||||
"regions": ["nyc3"],
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"volume_id": "98d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my snapshot",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
"created_at": "2002-10-02T15:00:00.05Z"
|
||||
},
|
||||
|
@ -326,19 +314,16 @@ func TestStorageSnapshots_Create(t *testing.T) {
|
|||
}
|
||||
|
||||
want := &Snapshot{
|
||||
Region: &Region{Slug: "nyc3"},
|
||||
Regions: []string{"nyc3"},
|
||||
ID: "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
VolumeID: "98d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
Name: "my snapshot",
|
||||
Description: "my description",
|
||||
SizeGigaBytes: 100,
|
||||
CreatedAt: time.Date(2002, 10, 02, 15, 00, 00, 50000000, time.UTC),
|
||||
Created: "2002-10-02T15:00:00.05Z",
|
||||
}
|
||||
jBlob := `{
|
||||
"snapshot":{
|
||||
"region": {"slug": "nyc3"},
|
||||
"regions": ["nyc3"],
|
||||
"id": "80d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"volume_id": "98d414c6-295e-4e3a-ac58-eb9456c1e1d1",
|
||||
"name": "my snapshot",
|
||||
"description": "my description",
|
||||
"size_gigabytes": 100,
|
||||
|
|
Loading…
Reference in New Issue