Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

storage: support for beta storage API #89

Merged
merged 1 commit into from
Jun 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions droplets.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type Droplet struct {
Created string `json:"created_at,omitempty"`
Kernel *Kernel `json:"kernel,omitempty"`
Tags []string `json:"tags,ommitempty"`
VolumeIDs []string `json:"volumes"`
}

// PublicIPv4 returns the public IPv4 address for the Droplet.
Expand Down Expand Up @@ -146,6 +147,27 @@ type DropletCreateImage struct {
Slug string
}

// DropletCreateVolume identifies a volume to attach for the create request. It
// prefers Name over ID,
type DropletCreateVolume struct {
ID string
Name string
}

// MarshalJSON returns an object with either the name or id of the volume. It
// returns the id if the name is empty.
func (d DropletCreateVolume) MarshalJSON() ([]byte, error) {
if d.Name != "" {
return json.Marshal(struct {
Name string `json:"name"`
}{Name: d.Name})
}

return json.Marshal(struct {
ID string `json:"id"`
}{ID: d.ID})
}

// MarshalJSON returns either the slug or id of the image. It returns the id
// if the slug is empty.
func (d DropletCreateImage) MarshalJSON() ([]byte, error) {
Expand Down Expand Up @@ -183,6 +205,7 @@ type DropletCreateRequest struct {
IPv6 bool `json:"ipv6"`
PrivateNetworking bool `json:"private_networking"`
UserData string `json:"user_data,omitempty"`
Volumes []DropletCreateVolume `json:"volumes,omitempty"`
}

// DropletMultiCreateRequest is a request to create multiple droplets.
Expand Down
30 changes: 20 additions & 10 deletions droplets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestDroplets_ListDroplets(t *testing.T) {

expected := []Droplet{{ID: 1}, {ID: 2}}
if !reflect.DeepEqual(droplets, expected) {
t.Errorf("Droplets.List returned %+v, expected %+v", droplets, expected)
t.Errorf("Droplets.List\n got=%#v\nwant=%#v", droplets, expected)
}
}

Expand Down Expand Up @@ -132,7 +132,7 @@ func TestDroplets_GetDroplet(t *testing.T) {

expected := &Droplet{ID: 12345}
if !reflect.DeepEqual(droplets, expected) {
t.Errorf("Droplets.Get returned %+v, expected %+v", droplets, expected)
t.Errorf("Droplets.Get\n got=%#v\nwant=%#v", droplets, expected)
}
}

Expand All @@ -147,6 +147,11 @@ func TestDroplets_Create(t *testing.T) {
Image: DropletCreateImage{
ID: 1,
},
Volumes: []DropletCreateVolume{
{Name: "hello-im-a-volume"},
{ID: "hello-im-another-volume"},
{Name: "hello-im-still-a-volume", ID: "should be ignored due to Name"},
},
}

mux.HandleFunc("/v2/droplets", func(w http.ResponseWriter, r *http.Request) {
Expand All @@ -159,6 +164,11 @@ func TestDroplets_Create(t *testing.T) {
"backups": false,
"ipv6": false,
"private_networking": false,
"volumes": []interface{}{
map[string]interface{}{"name": "hello-im-a-volume"},
map[string]interface{}{"id": "hello-im-another-volume"},
map[string]interface{}{"name": "hello-im-still-a-volume"},
},
}

var v map[string]interface{}
Expand All @@ -168,7 +178,7 @@ func TestDroplets_Create(t *testing.T) {
}

if !reflect.DeepEqual(v, expected) {
t.Errorf("Request body = %#v, expected %#v", v, expected)
t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected)
}

fmt.Fprintf(w, `{"droplet":{"id":1}, "links":{"actions": [{"id": 1, "href": "http://example.com", "rel": "create"}]}}`)
Expand Down Expand Up @@ -293,7 +303,7 @@ func TestDroplets_Kernels(t *testing.T) {

expected := []Kernel{{ID: 1}, {ID: 2}}
if !reflect.DeepEqual(kernels, expected) {
t.Errorf("Droplets.Kernels returned %+v, expected %+v", kernels, expected)
t.Errorf("Droplets.Kernels\n got=%#v\nwant=%#v", kernels, expected)
}
}

Expand All @@ -314,7 +324,7 @@ func TestDroplets_Snapshots(t *testing.T) {

expected := []Image{{ID: 1}, {ID: 2}}
if !reflect.DeepEqual(snapshots, expected) {
t.Errorf("Droplets.Snapshots returned %+v, expected %+v", snapshots, expected)
t.Errorf("Droplets.Snapshots\n got=%#v\nwant=%#v", snapshots, expected)
}
}

Expand All @@ -335,7 +345,7 @@ func TestDroplets_Backups(t *testing.T) {

expected := []Image{{ID: 1}, {ID: 2}}
if !reflect.DeepEqual(backups, expected) {
t.Errorf("Droplets.Backups returned %+v, expected %+v", backups, expected)
t.Errorf("Droplets.Backups\n got=%#v\nwant=%#v", backups, expected)
}
}

Expand All @@ -356,7 +366,7 @@ func TestDroplets_Actions(t *testing.T) {

expected := []Action{{ID: 1}, {ID: 2}}
if !reflect.DeepEqual(actions, expected) {
t.Errorf("Droplets.Actions returned %+v, expected %+v", actions, expected)
t.Errorf("Droplets.Actions\n got=%#v\nwant=%#v", actions, expected)
}
}

Expand All @@ -376,7 +386,7 @@ func TestDroplets_Neighbors(t *testing.T) {

expected := []Droplet{{ID: 1}, {ID: 2}}
if !reflect.DeepEqual(neighbors, expected) {
t.Errorf("Droplets.Neighbors returned %+v, expected %+v", neighbors, expected)
t.Errorf("Droplets.Neighbors\n got=%#v\nwant=%#v", neighbors, expected)
}
}

Expand All @@ -390,7 +400,7 @@ func TestNetworkV4_String(t *testing.T) {
stringified := network.String()
expected := `godo.NetworkV4{IPAddress:"192.168.1.2", Netmask:"255.255.255.0", Gateway:"192.168.1.1", Type:""}`
if expected != stringified {
t.Errorf("NetworkV4.String returned %+v, expected %+v", stringified, expected)
t.Errorf("NetworkV4.String\n got=%#v\nwant=%#v", stringified, expected)
}

}
Expand All @@ -404,7 +414,7 @@ func TestNetworkV6_String(t *testing.T) {
stringified := network.String()
expected := `godo.NetworkV6{IPAddress:"2604:A880:0800:0010:0000:0000:02DD:4001", Netmask:64, Gateway:"2604:A880:0800:0010:0000:0000:0000:0001", Type:""}`
if expected != stringified {
t.Errorf("NetworkV6.String returned %+v, expected %+v", stringified, expected)
t.Errorf("NetworkV6.String\n got=%#v\nwant=%#v", stringified, expected)
}
}

Expand Down
13 changes: 12 additions & 1 deletion godo.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ type Client struct {
Sizes SizesService
FloatingIPs FloatingIPsService
FloatingIPActions FloatingIPActionsService
Storage StorageService
StorageActions StorageActionsService
Tags TagsService

// Optional function called after every successful request made to the DO APIs
Expand Down Expand Up @@ -94,7 +96,10 @@ type ErrorResponse struct {
Response *http.Response

// Error message
Message string
Message string `json:"message"`

// RequestID returned from the API, useful to contact support.
RequestID string `json:"request_id"`
}

// Rate contains the rate limit for the current client.
Expand Down Expand Up @@ -157,6 +162,8 @@ func NewClient(httpClient *http.Client) *Client {
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}

return c
Expand Down Expand Up @@ -318,6 +325,10 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
return response, err
}
func (r *ErrorResponse) Error() string {
if r.RequestID != "" {
return fmt.Sprintf("%v %v: %d (request %q) %v",
r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.RequestID, r.Message)
}
return fmt.Sprintf("%v %v: %d %v",
r.Response.Request.Method, r.Response.Request.URL, r.Response.StatusCode, r.Message)
}
Expand Down
Loading