Skip to content

Commit

Permalink
Merge pull request digitalocean#108 from digitalocean/feature/snapshots
Browse files Browse the repository at this point in the history
Add snapshots endpoints.
  • Loading branch information
phillbaker authored Nov 30, 2016
2 parents 5c91372 + e715166 commit bec8984
Show file tree
Hide file tree
Showing 6 changed files with 336 additions and 55 deletions.
4 changes: 2 additions & 2 deletions droplets.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
Expand Down Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions godo.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type Client struct {
Sizes SizesService
FloatingIPs FloatingIPsService
FloatingIPActions FloatingIPActionsService
Snapshots SnapshotsService
Storage StorageService
StorageActions StorageActionsService
Tags TagsService
Expand Down Expand Up @@ -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}
Expand Down
136 changes: 136 additions & 0 deletions snapshots.go
Original file line number Diff line number Diff line change
@@ -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
}
179 changes: 179 additions & 0 deletions snapshots_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
Loading

0 comments on commit bec8984

Please sign in to comment.