Update digital ocean dependencies

Signed-off-by: David Gageot <david@gageot.net>
This commit is contained in:
David Gageot 2015-11-23 17:52:42 +01:00
parent 480051c1f7
commit 198ab07be0
25 changed files with 1172 additions and 182 deletions

4
Godeps/Godeps.json generated
View File

@ -68,8 +68,8 @@
},
{
"ImportPath": "github.com/digitalocean/godo",
"Comment": "v0.5.0",
"Rev": "5478aae80694de1d2d0e02c386bbedd201266234"
"Comment": "v0.9.0-8-g2124bf3",
"Rev": "2124bf3eeeb4ac070337bb19ef7b76a745de56f4"
},
{
"ImportPath": "github.com/docker/docker/pkg/term",

View File

@ -158,14 +158,14 @@ func (d *Driver) Create() error {
client := d.getClient()
createRequest := &godo.DropletCreateRequest{
Image: d.Image,
Image: godo.DropletCreateImage{Slug: d.Image},
Name: d.MachineName,
Region: d.Region,
Size: d.Size,
IPv6: d.IPv6,
PrivateNetworking: d.PrivateNetworking,
Backups: d.Backups,
SSHKeys: []interface{}{d.SSHKeyID},
SSHKeys: []godo.DropletCreateSSHKey{{ID: d.SSHKeyID}},
}
newDroplet, _, err := client.Droplets.Create(createRequest)
@ -173,7 +173,7 @@ func (d *Driver) Create() error {
return err
}
d.DropletID = newDroplet.Droplet.ID
d.DropletID = newDroplet.ID
log.Info("Waiting for IP address to be assigned to the Droplet...")
for {
@ -181,7 +181,7 @@ func (d *Driver) Create() error {
if err != nil {
return err
}
for _, network := range newDroplet.Droplet.Networks.V4 {
for _, network := range newDroplet.Networks.V4 {
if network.Type == "public" {
d.IPAddress = network.IPAddress
}
@ -195,7 +195,7 @@ func (d *Driver) Create() error {
}
log.Debugf("Created droplet ID %d, IP address %s",
newDroplet.Droplet.ID,
newDroplet.ID,
d.IPAddress)
return nil
@ -237,7 +237,7 @@ func (d *Driver) GetState() (state.State, error) {
if err != nil {
return state.Error, err
}
switch droplet.Droplet.Status {
switch droplet.Status {
case "new":
return state.Starting, nil
case "active":

6
vendor/github.com/digitalocean/godo/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,6 @@
language: go
go:
- 1.3
- 1.4
- tip

23
vendor/github.com/digitalocean/godo/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,23 @@
# Contributing
If you submit a pull request, please keep the following guidelines in mind:
1. Code should be `go fmt` compliant.
2. Types, structs and funcs should be documented.
3. Tests pass.
## Getting set up
Assuming your `$GOPATH` is set up according to your desires, run:
```sh
go get github.com/digitalocean/godo
```
## Running tests
When working on code in this repository, tests can be run via:
```sh
go test .
```

View File

@ -1,10 +0,0 @@
OPEN = $(shell which xdg-open || which gnome-open || which open)
cov:
@@gocov test | gocov-html > /tmp/coverage.html
@@${OPEN} /tmp/coverage.html
ci:
go get -d -v -t ./...
go build ./...
go test -v ./...

View File

@ -1,10 +1,12 @@
[![Build Status](https://travis-ci.org/digitalocean/godo.svg)](https://travis-ci.org/digitalocean/godo)
# Godo
Godo is a Go client library for accessing the DigitalOcean V2 API.
You can view the client API docs here: [http://godoc.org/github.com/digitalocean/godo](http://godoc.org/github.com/digitalocean/godo)
You can view Digital Ocean API docs here: [https://developers.digitalocean.com/v2/](https://developers.digitalocean.com/v2/)
You can view DigitalOcean API docs here: [https://developers.digitalocean.com/documentation/v2/](https://developers.digitalocean.com/documentation/v2/)
## Usage
@ -20,19 +22,30 @@ access different parts of the DigitalOcean API.
Currently, Personal Access Token (PAT) is the only method of
authenticating with the API. You can manage your tokens
at the Digital Ocean Control Panel [Applications Page](https://cloud.digitalocean.com/settings/applications).
at the DigitalOcean Control Panel [Applications Page](https://cloud.digitalocean.com/settings/applications).
You can then use your token to create a new client:
```go
import "code.google.com/p/goauth2/oauth"
import "golang.org/x/oauth2"
pat := "mytoken"
t := &oauth.Transport{
Token: &oauth.Token{AccessToken: pat},
type TokenSource struct {
AccessToken string
}
client := godo.NewClient(t.Client())
func (t *TokenSource) Token() (*oauth2.Token, error) {
token := &oauth2.Token{
AccessToken: t.AccessToken,
}
return token, nil
}
tokenSource := &TokenSource{
AccessToken: pat,
}
oauthClient := oauth2.NewClient(oauth2.NoContext, tokenSource)
client := godo.NewClient(oauthClient)
```
## Examples
@ -47,7 +60,9 @@ createRequest := &godo.DropletCreateRequest{
Name: dropletName,
Region: "nyc3",
Size: "512mb",
Image: "ubuntu-14-04-x64",
Image: godo.DropletCreateImage{
Slug: "ubuntu-14-04-x64",
},
}
newDroplet, _, err := client.Droplets.Create(createRequest)
@ -72,29 +87,50 @@ func DropletList(client *godo.Client) ([]godo.Droplet, error) {
for {
droplets, resp, err := client.Droplets.List(opt)
if err != nil {
return err
return nil, err
}
// append the current page's droplets to our list
for _, d := range droplets {
list = append(list, d)
}
// if we are at the last page, break out the for loop
if resp.Links.IsLastPage() {
break
}
page, err := resp.Links.CurrentPage()
if err != nil {
return err
// if we are at the last page, break out the for loop
if resp.Links == nil || resp.Links.IsLastPage() {
break
}
// set the page we want for the next request
opt.Page = page + 1
page, err := resp.Links.CurrentPage()
if err != nil {
return nil, err
}
// set the page we want for the next request
opt.Page = page + 1
}
return nil
return list, nil
}
```
## Versioning
Each version of the client is tagged and the version is updated accordingly.
Since Go does not have a built-in versioning, a package management tool is
recommended - a good one that works with git tags is
[gopkg.in](http://labix.org/gopkg.in).
To see the list of past versions, run `git tag`.
## Documentation
For a comprehensive list of examples, check out the [API documentation](https://developers.digitalocean.com/documentation/v2/).
For details on all the functionality in this library, see the [GoDoc](http://godoc.org/github.com/digitalocean/godo) documentation.
## Contributing
We love pull requests! Please see the [contribution guidelines](CONTRIBUTING.md).

52
vendor/github.com/digitalocean/godo/account.go generated vendored Normal file
View File

@ -0,0 +1,52 @@
package godo
// AccountService is an interface for interfacing with the Account
// endpoints of the DigitalOcean API
// See: https://developers.digitalocean.com/documentation/v2/#account
type AccountService interface {
Get() (*Account, *Response, error)
}
// AccountServiceOp handles communication with the Account related methods of
// the DigitalOcean API.
type AccountServiceOp struct {
client *Client
}
var _ AccountService = &AccountServiceOp{}
// Account represents a DigitalOcean Account
type Account struct {
DropletLimit int `json:"droplet_limit,omitempty"`
Email string `json:"email,omitempty"`
UUID string `json:"uuid,omitempty"`
EmailVerified bool `json:"email_verified,omitempty"`
Status string `json:"status,omitempty"`
StatusMessage string `json:"status_message,omitempty"`
}
type accountRoot struct {
Account *Account `json:"account"`
}
func (r Account) String() string {
return Stringify(r)
}
// Get DigitalOcean account info
func (s *AccountServiceOp) Get() (*Account, *Response, error) {
path := "v2/account"
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(accountRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return root.Account, resp, err
}

View File

@ -13,7 +13,7 @@ const (
)
// ActionsService handles communction with action related methods of the
// DigitalOcean API: https://developers.digitalocean.com/#actions
// DigitalOcean API: https://developers.digitalocean.com/documentation/v2#actions
type ActionsService interface {
List(*ListOptions) ([]Action, *Response, error)
Get(int) (*Action, *Response, error)
@ -25,6 +25,8 @@ type ActionsServiceOp struct {
client *Client
}
var _ ActionsService = &ActionsServiceOp{}
type actionsRoot struct {
Actions []Action `json:"actions"`
Links *Links `json:"links"`
@ -43,6 +45,8 @@ type Action struct {
CompletedAt *Timestamp `json:"completed_at"`
ResourceID int `json:"resource_id"`
ResourceType string `json:"resource_type"`
Region *Region `json:"region,omitempty"`
RegionSlug string `json:"region_slug,omitempty"`
}
// List all actions
@ -70,8 +74,12 @@ func (s *ActionsServiceOp) List(opt *ListOptions) ([]Action, *Response, error) {
return root.Actions, resp, err
}
// Get an action by ID
// Get an action by ID.
func (s *ActionsServiceOp) Get(id int) (*Action, *Response, error) {
if id < 1 {
return nil, nil, NewArgError("id", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d", actionsBasePath, id)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {

View File

@ -1,12 +0,0 @@
package godo
// ActionRequest reprents DigitalOcean Action Request
type ActionRequest struct {
Type string `json:"type"`
Params map[string]interface{} `json:"params,omitempty"`
}
// Converts an ActionRequest to a string.
func (d ActionRequest) String() string {
return Stringify(d)
}

View File

@ -4,13 +4,13 @@ import "fmt"
const domainsBasePath = "v2/domains"
// DomainsService is an interface for managing DNS with the Digital Ocean API.
// See: https://developers.digitalocean.com/#domains and
// https://developers.digitalocean.com/#domain-records
// DomainsService is an interface for managing DNS with the DigitalOcean API.
// See: https://developers.digitalocean.com/documentation/v2#domains and
// https://developers.digitalocean.com/documentation/v2#domain-records
type DomainsService interface {
List(*ListOptions) ([]Domain, *Response, error)
Get(string) (*DomainRoot, *Response, error)
Create(*DomainCreateRequest) (*DomainRoot, *Response, error)
Get(string) (*Domain, *Response, error)
Create(*DomainCreateRequest) (*Domain, *Response, error)
Delete(string) (*Response, error)
Records(string, *ListOptions) ([]DomainRecord, *Response, error)
@ -26,15 +26,17 @@ type DomainsServiceOp struct {
client *Client
}
// Domain represents a Digital Ocean domain
var _ DomainsService = &DomainsServiceOp{}
// Domain represents a DigitalOcean domain
type Domain struct {
Name string `json:"name"`
TTL int `json:"ttl"`
ZoneFile string `json:"zone_file"`
}
// DomainRoot represents a response from the Digital Ocean API
type DomainRoot struct {
// domainRoot represents a response from the DigitalOcean API
type domainRoot struct {
Domain *Domain `json:"domain"`
}
@ -50,12 +52,12 @@ type DomainCreateRequest struct {
}
// DomainRecordRoot is the root of an individual Domain Record response
type DomainRecordRoot struct {
type domainRecordRoot struct {
DomainRecord *DomainRecord `json:"domain_record"`
}
// DomainRecordsRoot is the root of a group of Domain Record responses
type DomainRecordsRoot struct {
type domainRecordsRoot struct {
DomainRecords []DomainRecord `json:"domain_records"`
Links *Links `json:"links"`
}
@ -85,7 +87,7 @@ func (d Domain) String() string {
return Stringify(d)
}
// List all domains
// List all domains.
func (s DomainsServiceOp) List(opt *ListOptions) ([]Domain, *Response, error) {
path := domainsBasePath
path, err := addOptions(path, opt)
@ -110,8 +112,12 @@ func (s DomainsServiceOp) List(opt *ListOptions) ([]Domain, *Response, error) {
return root.Domains, resp, err
}
// Get individual domain
func (s *DomainsServiceOp) Get(name string) (*DomainRoot, *Response, error) {
// Get individual domain. It requires a non-empty domain name.
func (s *DomainsServiceOp) Get(name string) (*Domain, *Response, error) {
if len(name) < 1 {
return nil, nil, NewArgError("name", "cannot be an empty string")
}
path := fmt.Sprintf("%s/%s", domainsBasePath, name)
req, err := s.client.NewRequest("GET", path, nil)
@ -119,17 +125,21 @@ func (s *DomainsServiceOp) Get(name string) (*DomainRoot, *Response, error) {
return nil, nil, err
}
root := new(DomainRoot)
root := new(domainRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return root, resp, err
return root.Domain, resp, err
}
// Create a new domain
func (s *DomainsServiceOp) Create(createRequest *DomainCreateRequest) (*DomainRoot, *Response, error) {
func (s *DomainsServiceOp) Create(createRequest *DomainCreateRequest) (*Domain, *Response, error) {
if createRequest == nil {
return nil, nil, NewArgError("createRequest", "cannot be nil")
}
path := domainsBasePath
req, err := s.client.NewRequest("POST", path, createRequest)
@ -137,17 +147,20 @@ func (s *DomainsServiceOp) Create(createRequest *DomainCreateRequest) (*DomainRo
return nil, nil, err
}
root := new(DomainRoot)
root := new(domainRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return root, resp, err
return root.Domain, resp, err
}
// Delete droplet
// Delete domain
func (s *DomainsServiceOp) Delete(name string) (*Response, error) {
if len(name) < 1 {
return nil, NewArgError("name", "cannot be an empty string")
}
path := fmt.Sprintf("%s/%s", domainsBasePath, name)
req, err := s.client.NewRequest("DELETE", path, nil)
@ -172,6 +185,10 @@ func (d DomainRecordEditRequest) String() string {
// Records returns a slice of DomainRecords for a domain
func (s *DomainsServiceOp) Records(domain string, opt *ListOptions) ([]DomainRecord, *Response, error) {
if len(domain) < 1 {
return nil, nil, NewArgError("domain", "cannot be an empty string")
}
path := fmt.Sprintf("%s/%s/records", domainsBasePath, domain)
path, err := addOptions(path, opt)
if err != nil {
@ -183,7 +200,7 @@ func (s *DomainsServiceOp) Records(domain string, opt *ListOptions) ([]DomainRec
return nil, nil, err
}
root := new(DomainRecordsRoot)
root := new(domainRecordsRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
@ -197,6 +214,14 @@ func (s *DomainsServiceOp) Records(domain string, opt *ListOptions) ([]DomainRec
// Record returns the record id from a domain
func (s *DomainsServiceOp) Record(domain string, id int) (*DomainRecord, *Response, error) {
if len(domain) < 1 {
return nil, nil, NewArgError("domain", "cannot be an empty string")
}
if id < 1 {
return nil, nil, NewArgError("id", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%s/records/%d", domainsBasePath, domain, id)
req, err := s.client.NewRequest("GET", path, nil)
@ -204,7 +229,7 @@ func (s *DomainsServiceOp) Record(domain string, id int) (*DomainRecord, *Respon
return nil, nil, err
}
record := new(DomainRecordRoot)
record := new(domainRecordRoot)
resp, err := s.client.Do(req, record)
if err != nil {
return nil, resp, err
@ -215,6 +240,14 @@ func (s *DomainsServiceOp) Record(domain string, id int) (*DomainRecord, *Respon
// DeleteRecord deletes a record from a domain identified by id
func (s *DomainsServiceOp) DeleteRecord(domain string, id int) (*Response, error) {
if len(domain) < 1 {
return nil, NewArgError("domain", "cannot be an empty string")
}
if id < 1 {
return nil, NewArgError("id", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%s/records/%d", domainsBasePath, domain, id)
req, err := s.client.NewRequest("DELETE", path, nil)
@ -231,7 +264,20 @@ func (s *DomainsServiceOp) DeleteRecord(domain string, id int) (*Response, error
func (s *DomainsServiceOp) EditRecord(
domain string,
id int,
editRequest *DomainRecordEditRequest) (*DomainRecord, *Response, error) {
editRequest *DomainRecordEditRequest,
) (*DomainRecord, *Response, error) {
if len(domain) < 1 {
return nil, nil, NewArgError("domain", "cannot be an empty string")
}
if id < 1 {
return nil, nil, NewArgError("id", "cannot be less than 1")
}
if editRequest == nil {
return nil, nil, NewArgError("editRequest", "cannot be nil")
}
path := fmt.Sprintf("%s/%s/records/%d", domainsBasePath, domain, id)
req, err := s.client.NewRequest("PUT", path, editRequest)
@ -252,6 +298,14 @@ func (s *DomainsServiceOp) EditRecord(
func (s *DomainsServiceOp) CreateRecord(
domain string,
createRequest *DomainRecordEditRequest) (*DomainRecord, *Response, error) {
if len(domain) < 1 {
return nil, nil, NewArgError("domain", "cannot be empty string")
}
if createRequest == nil {
return nil, nil, NewArgError("createRequest", "cannot be nil")
}
path := fmt.Sprintf("%s/%s/records", domainsBasePath, domain)
req, err := s.client.NewRequest("POST", path, createRequest)
@ -259,7 +313,7 @@ func (s *DomainsServiceOp) CreateRecord(
return nil, nil, err
}
d := new(DomainRecordRoot)
d := new(domainRecordRoot)
resp, err := s.client.Do(req, d)
if err != nil {
return nil, resp, err

View File

@ -5,9 +5,12 @@ import (
"net/url"
)
// ActionRequest reprents DigitalOcean Action Request
type ActionRequest map[string]interface{}
// DropletActionsService is an interface for interfacing with the droplet actions
// endpoints of the Digital Ocean API
// See: https://developers.digitalocean.com/#droplet-actions
// endpoints of the DigitalOcean API
// See: https://developers.digitalocean.com/documentation/v2#droplet-actions
type DropletActionsService interface {
Shutdown(int) (*Action, *Response, error)
PowerOff(int) (*Action, *Response, error)
@ -15,9 +18,18 @@ type DropletActionsService interface {
PowerCycle(int) (*Action, *Response, error)
Reboot(int) (*Action, *Response, error)
Restore(int, int) (*Action, *Response, error)
Resize(int, string) (*Action, *Response, error)
Resize(int, string, bool) (*Action, *Response, error)
Rename(int, string) (*Action, *Response, error)
doAction(int, *ActionRequest) (*Action, *Response, error)
Snapshot(int, string) (*Action, *Response, error)
EnableBackups(int) (*Action, *Response, error)
DisableBackups(int) (*Action, *Response, error)
PasswordReset(int) (*Action, *Response, error)
RebuildByImageID(int, int) (*Action, *Response, error)
RebuildByImageSlug(int, string) (*Action, *Response, error)
ChangeKernel(int, int) (*Action, *Response, error)
EnableIPv6(int) (*Action, *Response, error)
EnablePrivateNetworking(int) (*Action, *Response, error)
Upgrade(int) (*Action, *Response, error)
Get(int, int) (*Action, *Response, error)
GetByURI(string) (*Action, *Response, error)
}
@ -28,79 +40,142 @@ type DropletActionsServiceOp struct {
client *Client
}
var _ DropletActionsService = &DropletActionsServiceOp{}
// Shutdown a Droplet
func (s *DropletActionsServiceOp) Shutdown(id int) (*Action, *Response, error) {
request := &ActionRequest{Type: "shutdown"}
request := &ActionRequest{"type": "shutdown"}
return s.doAction(id, request)
}
// PowerOff a Droplet
func (s *DropletActionsServiceOp) PowerOff(id int) (*Action, *Response, error) {
request := &ActionRequest{Type: "power_off"}
request := &ActionRequest{"type": "power_off"}
return s.doAction(id, request)
}
// PowerOn a Droplet
func (s *DropletActionsServiceOp) PowerOn(id int) (*Action, *Response, error) {
request := &ActionRequest{Type: "power_on"}
request := &ActionRequest{"type": "power_on"}
return s.doAction(id, request)
}
// PowerCycle a Droplet
func (s *DropletActionsServiceOp) PowerCycle(id int) (*Action, *Response, error) {
request := &ActionRequest{Type: "power_cycle"}
request := &ActionRequest{"type": "power_cycle"}
return s.doAction(id, request)
}
// Reboot a Droplet
func (s *DropletActionsServiceOp) Reboot(id int) (*Action, *Response, error) {
request := &ActionRequest{Type: "reboot"}
request := &ActionRequest{"type": "reboot"}
return s.doAction(id, request)
}
// Restore an image to a Droplet
func (s *DropletActionsServiceOp) Restore(id, imageID int) (*Action, *Response, error) {
options := map[string]interface{}{
"image": float64(imageID),
}
requestType := "restore"
request := &ActionRequest{
Type: requestType,
Params: options,
"type": requestType,
"image": float64(imageID),
}
return s.doAction(id, request)
}
// Resize a Droplet
func (s *DropletActionsServiceOp) Resize(id int, sizeSlug string) (*Action, *Response, error) {
options := map[string]interface{}{
"size": sizeSlug,
}
func (s *DropletActionsServiceOp) Resize(id int, sizeSlug string, resizeDisk bool) (*Action, *Response, error) {
requestType := "resize"
request := &ActionRequest{
Type: requestType,
Params: options,
"type": requestType,
"size": sizeSlug,
"disk": resizeDisk,
}
return s.doAction(id, request)
}
// Rename a Droplet
func (s *DropletActionsServiceOp) Rename(id int, name string) (*Action, *Response, error) {
options := map[string]interface{}{
"name": name,
}
requestType := "rename"
request := &ActionRequest{
Type: requestType,
Params: options,
"type": requestType,
"name": name,
}
return s.doAction(id, request)
}
// Snapshot a Droplet.
func (s *DropletActionsServiceOp) Snapshot(id int, name string) (*Action, *Response, error) {
requestType := "snapshot"
request := &ActionRequest{
"type": requestType,
"name": name,
}
return s.doAction(id, request)
}
// EnableBackups enables backups for a droplet.
func (s *DropletActionsServiceOp) EnableBackups(id int) (*Action, *Response, error) {
request := &ActionRequest{"type": "enable_backups"}
return s.doAction(id, request)
}
// DisableBackups disables backups for a droplet.
func (s *DropletActionsServiceOp) DisableBackups(id int) (*Action, *Response, error) {
request := &ActionRequest{"type": "disable_backups"}
return s.doAction(id, request)
}
// PasswordReset resets the password for a droplet.
func (s *DropletActionsServiceOp) PasswordReset(id int) (*Action, *Response, error) {
request := &ActionRequest{"type": "password_reset"}
return s.doAction(id, request)
}
// RebuildByImageID rebuilds a droplet droplet from an image with a given id.
func (s *DropletActionsServiceOp) RebuildByImageID(id, imageID int) (*Action, *Response, error) {
request := &ActionRequest{"type": "rebuild", "image": imageID}
return s.doAction(id, request)
}
// RebuildByImageSlug rebuilds a droplet from an image with a given slug.
func (s *DropletActionsServiceOp) RebuildByImageSlug(id int, slug string) (*Action, *Response, error) {
request := &ActionRequest{"type": "rebuild", "image": slug}
return s.doAction(id, request)
}
// ChangeKernel changes the kernel for a droplet.
func (s *DropletActionsServiceOp) ChangeKernel(id, kernelID int) (*Action, *Response, error) {
request := &ActionRequest{"type": "change_kernel", "kernel": kernelID}
return s.doAction(id, request)
}
// EnableIPv6 enables IPv6 for a droplet.
func (s *DropletActionsServiceOp) EnableIPv6(id int) (*Action, *Response, error) {
request := &ActionRequest{"type": "enable_ipv6"}
return s.doAction(id, request)
}
// EnablePrivateNetworking enables private networking for a droplet.
func (s *DropletActionsServiceOp) EnablePrivateNetworking(id int) (*Action, *Response, error) {
request := &ActionRequest{"type": "enable_private_networking"}
return s.doAction(id, request)
}
// Upgrade a droplet.
func (s *DropletActionsServiceOp) Upgrade(id int) (*Action, *Response, error) {
request := &ActionRequest{"type": "upgrade"}
return s.doAction(id, request)
}
func (s *DropletActionsServiceOp) doAction(id int, request *ActionRequest) (*Action, *Response, error) {
if id < 1 {
return nil, nil, NewArgError("id", "cannot be less than 1")
}
if request == nil {
return nil, nil, NewArgError("request", "request can't be nil")
}
path := dropletActionPath(id)
req, err := s.client.NewRequest("POST", path, request)
@ -119,6 +194,14 @@ func (s *DropletActionsServiceOp) doAction(id int, request *ActionRequest) (*Act
// Get an action for a particular droplet by id.
func (s *DropletActionsServiceOp) Get(dropletID, actionID int) (*Action, *Response, error) {
if dropletID < 1 {
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
}
if actionID < 1 {
return nil, nil, NewArgError("actionID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d", dropletActionPath(dropletID), actionID)
return s.get(path)
}

View File

@ -1,18 +1,25 @@
package godo
import "fmt"
import (
"encoding/json"
"fmt"
)
const dropletBasePath = "v2/droplets"
// DropletsService is an interface for interfacing with the droplet
// endpoints of the Digital Ocean API
// See: https://developers.digitalocean.com/#droplets
// endpoints of the DigitalOcean API
// See: https://developers.digitalocean.com/documentation/v2#droplets
type DropletsService interface {
List(*ListOptions) ([]Droplet, *Response, error)
Get(int) (*DropletRoot, *Response, error)
Create(*DropletCreateRequest) (*DropletRoot, *Response, error)
Get(int) (*Droplet, *Response, error)
Create(*DropletCreateRequest) (*Droplet, *Response, error)
Delete(int) (*Response, error)
dropletActionStatus(string) (string, error)
Kernels(int, *ListOptions) ([]Kernel, *Response, error)
Snapshots(int, *ListOptions) ([]Image, *Response, error)
Backups(int, *ListOptions) ([]Image, *Response, error)
Actions(int, *ListOptions) ([]Action, *Response, error)
Neighbors(int) ([]Droplet, *Response, error)
}
// DropletsServiceOp handles communication with the droplet related methods of the
@ -21,6 +28,8 @@ type DropletsServiceOp struct {
client *Client
}
var _ DropletsService = &DropletsServiceOp{}
// Droplet represents a DigitalOcean Droplet
type Droplet struct {
ID int `json:"id,float64,omitempty"`
@ -31,12 +40,21 @@ type Droplet struct {
Region *Region `json:"region,omitempty"`
Image *Image `json:"image,omitempty"`
Size *Size `json:"size,omitempty"`
SizeSlug string `json:"size_slug,omitempty"`
BackupIDs []int `json:"backup_ids,omitempty"`
SnapshotIDs []int `json:"snapshot_ids,omitempty"`
Locked bool `json:"locked,bool,omitempty"`
Status string `json:"status,omitempty"`
Networks *Networks `json:"networks,omitempty"`
ActionIDs []int `json:"action_ids,omitempty"`
Created string `json:"created_at,omitempty"`
}
// Kernel object
type Kernel struct {
ID int `json:"id,float64,omitempty"`
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
}
// Convert Droplet to a string
@ -45,7 +63,7 @@ func (d Droplet) String() string {
}
// DropletRoot represents a Droplet root
type DropletRoot struct {
type dropletRoot struct {
Droplet *Droplet `json:"droplet"`
Links *Links `json:"links,omitempty"`
}
@ -55,17 +73,64 @@ type dropletsRoot struct {
Links *Links `json:"links"`
}
type kernelsRoot struct {
Kernels []Kernel `json:"kernels,omitempty"`
Links *Links `json:"links"`
}
type snapshotsRoot struct {
Snapshots []Image `json:"snapshots,omitempty"`
Links *Links `json:"links"`
}
type backupsRoot struct {
Backups []Image `json:"backups,omitempty"`
Links *Links `json:"links"`
}
// DropletCreateImage identifies an image for the create request. It prefers slug over ID.
type DropletCreateImage struct {
ID int
Slug string
}
// 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) {
if d.Slug != "" {
return json.Marshal(d.Slug)
}
return json.Marshal(d.ID)
}
// DropletCreateSSHKey identifies a SSH Key for the create request. It prefers fingerprint over ID.
type DropletCreateSSHKey struct {
ID int
Fingerprint string
}
// MarshalJSON returns either the fingerprint or id of the ssh key. It returns
// the id if the fingerprint is empty.
func (d DropletCreateSSHKey) MarshalJSON() ([]byte, error) {
if d.Fingerprint != "" {
return json.Marshal(d.Fingerprint)
}
return json.Marshal(d.ID)
}
// DropletCreateRequest represents a request to create a droplet.
type DropletCreateRequest struct {
Name string `json:"name"`
Region string `json:"region"`
Size string `json:"size"`
Image string `json:"image"`
SSHKeys []interface{} `json:"ssh_keys"`
Backups bool `json:"backups"`
IPv6 bool `json:"ipv6"`
PrivateNetworking bool `json:"private_networking"`
UserData string `json:"user_data"`
Name string `json:"name"`
Region string `json:"region"`
Size string `json:"size"`
Image DropletCreateImage `json:"image"`
SSHKeys []DropletCreateSSHKey `json:"ssh_keys"`
Backups bool `json:"backups"`
IPv6 bool `json:"ipv6"`
PrivateNetworking bool `json:"private_networking"`
UserData string `json:"user_data,omitempty"`
}
func (d DropletCreateRequest) String() string {
@ -74,19 +139,31 @@ func (d DropletCreateRequest) String() string {
// Networks represents the droplet's networks
type Networks struct {
V4 []Network `json:"v4,omitempty"`
V6 []Network `json:"v6,omitempty"`
V4 []NetworkV4 `json:"v4,omitempty"`
V6 []NetworkV6 `json:"v6,omitempty"`
}
// Network represents a DigitalOcean Network
type Network struct {
// NetworkV4 represents a DigitalOcean IPv4 Network
type NetworkV4 struct {
IPAddress string `json:"ip_address,omitempty"`
Netmask string `json:"netmask,omitempty"`
Gateway string `json:"gateway,omitempty"`
Type string `json:"type,omitempty"`
}
func (n Network) String() string {
func (n NetworkV4) String() string {
return Stringify(n)
}
// NetworkV6 represents a DigitalOcean IPv6 network.
type NetworkV6 struct {
IPAddress string `json:"ip_address,omitempty"`
Netmask int `json:"netmask,omitempty"`
Gateway string `json:"gateway,omitempty"`
Type string `json:"type,omitempty"`
}
func (n NetworkV6) String() string {
return Stringify(n)
}
@ -116,7 +193,11 @@ func (s *DropletsServiceOp) List(opt *ListOptions) ([]Droplet, *Response, error)
}
// Get individual droplet
func (s *DropletsServiceOp) Get(dropletID int) (*DropletRoot, *Response, error) {
func (s *DropletsServiceOp) Get(dropletID int) (*Droplet, *Response, error) {
if dropletID < 1 {
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d", dropletBasePath, dropletID)
req, err := s.client.NewRequest("GET", path, nil)
@ -124,17 +205,21 @@ func (s *DropletsServiceOp) Get(dropletID int) (*DropletRoot, *Response, error)
return nil, nil, err
}
root := new(DropletRoot)
root := new(dropletRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return root, resp, err
return root.Droplet, resp, err
}
// Create droplet
func (s *DropletsServiceOp) Create(createRequest *DropletCreateRequest) (*DropletRoot, *Response, error) {
func (s *DropletsServiceOp) Create(createRequest *DropletCreateRequest) (*Droplet, *Response, error) {
if createRequest == nil {
return nil, nil, NewArgError("createRequest", "cannot be nil")
}
path := dropletBasePath
req, err := s.client.NewRequest("POST", path, createRequest)
@ -142,7 +227,7 @@ func (s *DropletsServiceOp) Create(createRequest *DropletCreateRequest) (*Drople
return nil, nil, err
}
root := new(DropletRoot)
root := new(dropletRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
@ -151,11 +236,15 @@ func (s *DropletsServiceOp) Create(createRequest *DropletCreateRequest) (*Drople
resp.Links = l
}
return root, resp, err
return root.Droplet, resp, err
}
// Delete droplet
func (s *DropletsServiceOp) Delete(dropletID int) (*Response, error) {
if dropletID < 1 {
return nil, NewArgError("dropletID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d", dropletBasePath, dropletID)
req, err := s.client.NewRequest("DELETE", path, nil)
@ -168,6 +257,141 @@ func (s *DropletsServiceOp) Delete(dropletID int) (*Response, error) {
return resp, err
}
// Kernels lists kernels available for a droplet.
func (s *DropletsServiceOp) Kernels(dropletID int, opt *ListOptions) ([]Kernel, *Response, error) {
if dropletID < 1 {
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d/kernels", dropletBasePath, dropletID)
path, err := addOptions(path, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(kernelsRoot)
resp, err := s.client.Do(req, root)
if l := root.Links; l != nil {
resp.Links = l
}
return root.Kernels, resp, err
}
// Actions lists the actions for a droplet.
func (s *DropletsServiceOp) Actions(dropletID int, opt *ListOptions) ([]Action, *Response, error) {
if dropletID < 1 {
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d/actions", dropletBasePath, dropletID)
path, err := addOptions(path, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(actionsRoot)
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.Actions, resp, err
}
// Backups lists the backups for a droplet.
func (s *DropletsServiceOp) Backups(dropletID int, opt *ListOptions) ([]Image, *Response, error) {
if dropletID < 1 {
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d/backups", dropletBasePath, dropletID)
path, err := addOptions(path, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(backupsRoot)
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.Backups, resp, err
}
// Snapshots lists the snapshots available for a droplet.
func (s *DropletsServiceOp) Snapshots(dropletID int, opt *ListOptions) ([]Image, *Response, error) {
if dropletID < 1 {
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d/snapshots", dropletBasePath, dropletID)
path, err := addOptions(path, opt)
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
}
// Neighbors lists the neighbors for a droplet.
func (s *DropletsServiceOp) Neighbors(dropletID int) ([]Droplet, *Response, error) {
if dropletID < 1 {
return nil, nil, NewArgError("dropletID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d/neighbors", dropletBasePath, dropletID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(dropletsRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return root.Droplets, resp, err
}
func (s *DropletsServiceOp) dropletActionStatus(uri string) (string, error) {
action, _, err := s.client.DropletActions.GetByURI(uri)

24
vendor/github.com/digitalocean/godo/errors.go generated vendored Normal file
View File

@ -0,0 +1,24 @@
package godo
import "fmt"
// ArgError is an error that represents an error with an input to godo. It
// identifies the argument and the cause (if possible).
type ArgError struct {
arg string
reason string
}
var _ error = &ArgError{}
// NewArgError creates an InputError.
func NewArgError(arg, reason string) *ArgError {
return &ArgError{
arg: arg,
reason: reason,
}
}
func (e *ArgError) Error() string {
return fmt.Sprintf("%s is invalid because %s", e.arg, e.reason)
}

131
vendor/github.com/digitalocean/godo/floating_ips.go generated vendored Normal file
View File

@ -0,0 +1,131 @@
package godo
import "fmt"
const floatingBasePath = "v2/floating_ips"
// FloatingIPsService is an interface for interfacing with the floating IPs
// endpoints of the Digital Ocean API.
// See: https://developers.digitalocean.com/documentation/v2#floating-ips
type FloatingIPsService interface {
List(*ListOptions) ([]FloatingIP, *Response, error)
Get(string) (*FloatingIP, *Response, error)
Create(*FloatingIPCreateRequest) (*FloatingIP, *Response, error)
Delete(string) (*Response, error)
}
// FloatingIPsServiceOp handles communication with the floating IPs related methods of the
// DigitalOcean API.
type FloatingIPsServiceOp struct {
client *Client
}
var _ FloatingIPsService = &FloatingIPsServiceOp{}
// FloatingIP represents a Digital Ocean floating IP.
type FloatingIP struct {
Region *Region `json:"region"`
Droplet *Droplet `json:"droplet"`
IP string `json:"ip"`
}
func (f FloatingIP) String() string {
return Stringify(f)
}
type floatingIPsRoot struct {
FloatingIPs []FloatingIP `json:"floating_ips"`
Links *Links `json:"links"`
}
type floatingIPRoot struct {
FloatingIP *FloatingIP `json:"floating_ip"`
Links *Links `json:"links,omitempty"`
}
// FloatingIPCreateRequest represents a request to create a floating IP.
// If DropletID is not empty, the floating IP will be assigned to the
// droplet.
type FloatingIPCreateRequest struct {
Region string `json:"region"`
DropletID int `json:"droplet_id,omitempty"`
}
// List all floating IPs.
func (f *FloatingIPsServiceOp) List(opt *ListOptions) ([]FloatingIP, *Response, error) {
path := floatingBasePath
path, err := addOptions(path, opt)
if err != nil {
return nil, nil, err
}
req, err := f.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(floatingIPsRoot)
resp, err := f.client.Do(req, root)
if err != nil {
return nil, resp, err
}
if l := root.Links; l != nil {
resp.Links = l
}
return root.FloatingIPs, resp, err
}
// Get an individual floating IP.
func (f *FloatingIPsServiceOp) Get(ip string) (*FloatingIP, *Response, error) {
path := fmt.Sprintf("%s/%s", floatingBasePath, ip)
req, err := f.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(floatingIPRoot)
resp, err := f.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return root.FloatingIP, resp, err
}
// Create a floating IP. If the DropletID field of the request is not empty,
// the floating IP will also be assigned to the droplet.
func (f *FloatingIPsServiceOp) Create(createRequest *FloatingIPCreateRequest) (*FloatingIP, *Response, error) {
path := floatingBasePath
req, err := f.client.NewRequest("POST", path, createRequest)
if err != nil {
return nil, nil, err
}
root := new(floatingIPRoot)
resp, err := f.client.Do(req, root)
if err != nil {
return nil, resp, err
}
if l := root.Links; l != nil {
resp.Links = l
}
return root.FloatingIP, resp, err
}
// Delete a floating IP.
func (f *FloatingIPsServiceOp) Delete(ip string) (*Response, error) {
path := fmt.Sprintf("%s/%s", floatingBasePath, ip)
req, err := f.client.NewRequest("DELETE", path, nil)
if err != nil {
return nil, err
}
resp, err := f.client.Do(req, nil)
return resp, err
}

View File

@ -0,0 +1,97 @@
package godo
import "fmt"
// FloatingIPActionsService is an interface for interfacing with the
// floating IPs actions endpoints of the Digital Ocean API.
// See: https://developers.digitalocean.com/documentation/v2#floating-ips-action
type FloatingIPActionsService interface {
Assign(ip string, dropletID int) (*Action, *Response, error)
Unassign(ip string) (*Action, *Response, error)
Get(ip string, actionID int) (*Action, *Response, error)
List(ip string) ([]Action, *Response, error)
}
// FloatingIPActionsServiceOp handles communication with the floating IPs
// action related methods of the DigitalOcean API.
type FloatingIPActionsServiceOp struct {
client *Client
}
// Assign a floating IP to a droplet.
func (s *FloatingIPActionsServiceOp) Assign(ip string, dropletID int) (*Action, *Response, error) {
request := &ActionRequest{
"type": "assign",
"droplet_id": dropletID,
}
return s.doAction(ip, request)
}
// Unassign a floating IP from the droplet it is currently assigned to.
func (s *FloatingIPActionsServiceOp) Unassign(ip string) (*Action, *Response, error) {
request := &ActionRequest{"type": "unassign"}
return s.doAction(ip, request)
}
// Get an action for a particular floating IP by id.
func (s *FloatingIPActionsServiceOp) Get(ip string, actionID int) (*Action, *Response, error) {
path := fmt.Sprintf("%s/%d", floatingIPActionPath(ip), actionID)
return s.get(path)
}
// List the actions for a particular floating IP.
func (s *FloatingIPActionsServiceOp) List(ip string) ([]Action, *Response, error) {
path := floatingIPActionPath(ip)
return s.list(path)
}
func (s *FloatingIPActionsServiceOp) doAction(ip string, request *ActionRequest) (*Action, *Response, error) {
path := floatingIPActionPath(ip)
req, err := s.client.NewRequest("POST", path, request)
if err != nil {
return nil, nil, err
}
root := new(actionRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return &root.Event, resp, err
}
func (s *FloatingIPActionsServiceOp) get(path string) (*Action, *Response, error) {
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(actionRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return &root.Event, resp, err
}
func (s *FloatingIPActionsServiceOp) list(path string) ([]Action, *Response, error) {
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(actionsRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return root.Actions, resp, err
}
func floatingIPActionPath(ip string) string {
return fmt.Sprintf("%s/%s/actions", floatingBasePath, ip)
}

View File

@ -43,17 +43,26 @@ type Client struct {
Rate Rate
// Services used for communicating with the API
Actions ActionsService
Domains DomainsService
Droplets DropletsService
DropletActions DropletActionsService
Images ImagesService
ImageActions ImageActionsService
Keys KeysService
Regions RegionsService
Sizes SizesService
Account AccountService
Actions ActionsService
Domains DomainsService
Droplets DropletsService
DropletActions DropletActionsService
Images ImagesService
ImageActions ImageActionsService
Keys KeysService
Regions RegionsService
Sizes SizesService
FloatingIPs FloatingIPsService
FloatingIPActions FloatingIPActionsService
// Optional function called after every successful request made to the DO APIs
onRequestCompleted RequestCompletionCallback
}
// RequestCompletionCallback defines the type of the request callback function
type RequestCompletionCallback func(*http.Request, *http.Response)
// ListOptions specifies the optional parameters to various List methods that
// support pagination.
type ListOptions struct {
@ -64,7 +73,7 @@ type ListOptions struct {
PerPage int `url:"per_page,omitempty"`
}
// Response is a Digital Ocean response. This wraps the standard http.Response returned from DigitalOcean.
// Response is a DigitalOcean response. This wraps the standard http.Response returned from DigitalOcean.
type Response struct {
*http.Response
@ -106,21 +115,27 @@ func addOptions(s string, opt interface{}) (string, error) {
return s, nil
}
u, err := url.Parse(s)
origURL, err := url.Parse(s)
if err != nil {
return s, err
}
qv, err := query.Values(opt)
origValues := origURL.Query()
newValues, err := query.Values(opt)
if err != nil {
return s, err
}
u.RawQuery = qv.Encode()
return u.String(), nil
for k, v := range newValues {
origValues[k] = v
}
origURL.RawQuery = origValues.Encode()
return origURL.String(), nil
}
// NewClient returns a new Digital Ocean API client.
// NewClient returns a new DigitalOcean API client.
func NewClient(httpClient *http.Client) *Client {
if httpClient == nil {
httpClient = http.DefaultClient
@ -129,6 +144,7 @@ func NewClient(httpClient *http.Client) *Client {
baseURL, _ := url.Parse(defaultBaseURL)
c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent}
c.Account = &AccountServiceOp{client: c}
c.Actions = &ActionsServiceOp{client: c}
c.Domains = &DomainsServiceOp{client: c}
c.Droplets = &DropletsServiceOp{client: c}
@ -138,6 +154,8 @@ func NewClient(httpClient *http.Client) *Client {
c.Keys = &KeysServiceOp{client: c}
c.Regions = &RegionsServiceOp{client: c}
c.Sizes = &SizesServiceOp{client: c}
c.FloatingIPs = &FloatingIPsServiceOp{client: c}
c.FloatingIPActions = &FloatingIPActionsServiceOp{client: c}
return c
}
@ -172,6 +190,11 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
return req, nil
}
// OnRequestCompleted sets the DO API request completion callback
func (c *Client) OnRequestCompleted(rc RequestCompletionCallback) {
c.onRequestCompleted = rc
}
// newResponse creates a new Response for the provided http.Response
func newResponse(r *http.Response) *Response {
response := Response{Response: r}
@ -222,8 +245,15 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
if err != nil {
return nil, err
}
if c.onRequestCompleted != nil {
c.onRequestCompleted(req, resp)
}
defer resp.Body.Close()
defer func() {
if rerr := resp.Body.Close(); err == nil {
err = rerr
}
}()
response := newResponse(resp)
c.Rate = response.Rate
@ -235,9 +265,15 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
if v != nil {
if w, ok := v.(io.Writer); ok {
io.Copy(w, resp.Body)
_, err := io.Copy(w, resp.Body)
if err != nil {
return nil, err
}
} else {
json.NewDecoder(resp.Body).Decode(v)
err := json.NewDecoder(resp.Body).Decode(v)
if err != nil {
return nil, err
}
}
}
@ -259,7 +295,10 @@ func CheckResponse(r *http.Response) error {
errorResponse := &ErrorResponse{Response: r}
data, err := ioutil.ReadAll(r.Body)
if err == nil && len(data) > 0 {
json.Unmarshal(data, errorResponse)
err := json.Unmarshal(data, errorResponse)
if err != nil {
return err
}
}
return errorResponse
@ -297,6 +336,6 @@ func Bool(v bool) *bool {
// StreamToString converts a reader to a string
func StreamToString(stream io.Reader) string {
buf := new(bytes.Buffer)
buf.ReadFrom(stream)
_, _ = buf.ReadFrom(stream)
return buf.String()
}

View File

@ -3,8 +3,8 @@ package godo
import "fmt"
// ImageActionsService is an interface for interfacing with the image actions
// endpoints of the Digital Ocean API
// See: https://developers.digitalocean.com/#image-actions
// endpoints of the DigitalOcean API
// See: https://developers.digitalocean.com/documentation/v2#image-actions
type ImageActionsService interface {
Get(int, int) (*Action, *Response, error)
Transfer(int, *ActionRequest) (*Action, *Response, error)
@ -16,8 +16,18 @@ type ImageActionsServiceOp struct {
client *Client
}
var _ ImageActionsService = &ImageActionsServiceOp{}
// Transfer an image
func (i *ImageActionsServiceOp) Transfer(imageID int, transferRequest *ActionRequest) (*Action, *Response, error) {
if imageID < 1 {
return nil, nil, NewArgError("imageID", "cannot be less than 1")
}
if transferRequest == nil {
return nil, nil, NewArgError("transferRequest", "cannot be nil")
}
path := fmt.Sprintf("v2/images/%d/actions", imageID)
req, err := i.client.NewRequest("POST", path, transferRequest)
@ -36,6 +46,14 @@ func (i *ImageActionsServiceOp) Transfer(imageID int, transferRequest *ActionReq
// Get an action for a particular image by id.
func (i *ImageActionsServiceOp) Get(imageID, actionID int) (*Action, *Response, error) {
if imageID < 1 {
return nil, nil, NewArgError("imageID", "cannot be less than 1")
}
if actionID < 1 {
return nil, nil, NewArgError("actionID", "cannot be less than 1")
}
path := fmt.Sprintf("v2/images/%d/actions/%d", imageID, actionID)
req, err := i.client.NewRequest("GET", path, nil)

View File

@ -1,10 +1,21 @@
package godo
import "fmt"
const imageBasePath = "v2/images"
// ImagesService is an interface for interfacing with the images
// endpoints of the Digital Ocean API
// See: https://developers.digitalocean.com/#images
// endpoints of the DigitalOcean API
// See: https://developers.digitalocean.com/documentation/v2#images
type ImagesService interface {
List(*ListOptions) ([]Image, *Response, error)
ListDistribution(opt *ListOptions) ([]Image, *Response, error)
ListApplication(opt *ListOptions) ([]Image, *Response, error)
ListUser(opt *ListOptions) ([]Image, *Response, error)
GetByID(int) (*Image, *Response, error)
GetBySlug(string) (*Image, *Response, error)
Update(int, *ImageUpdateRequest) (*Image, *Response, error)
Delete(int) (*Response, error)
}
// ImagesServiceOp handles communication with the image related methods of the
@ -13,14 +24,24 @@ type ImagesServiceOp struct {
client *Client
}
var _ ImagesService = &ImagesServiceOp{}
// Image represents a DigitalOcean Image
type Image struct {
ID int `json:"id,float64,omitempty"`
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"`
Distribution string `json:"distribution,omitempty"`
Slug string `json:"slug,omitempty"`
Public bool `json:"public,omitempty"`
Regions []string `json:"regions,omitempty"`
MinDiskSize int `json:"min_disk_size,omitempty"`
Created string `json:"created_at,omitempty"`
}
// ImageUpdateRequest represents a request to update an image.
type ImageUpdateRequest struct {
Name string `json:"name"`
}
type imageRoot struct {
@ -32,17 +53,128 @@ type imagesRoot struct {
Links *Links `json:"links"`
}
type listImageOptions struct {
Private bool `url:"private,omitempty"`
Type string `url:"type,omitempty"`
}
func (i Image) String() string {
return Stringify(i)
}
// List all sizes
// List lists all the images available.
func (s *ImagesServiceOp) List(opt *ListOptions) ([]Image, *Response, error) {
path := "v2/images"
return s.list(opt, nil)
}
// ListDistribution lists all the distribution images.
func (s *ImagesServiceOp) ListDistribution(opt *ListOptions) ([]Image, *Response, error) {
listOpt := listImageOptions{Type: "distribution"}
return s.list(opt, &listOpt)
}
// ListApplication lists all the application images.
func (s *ImagesServiceOp) ListApplication(opt *ListOptions) ([]Image, *Response, error) {
listOpt := listImageOptions{Type: "application"}
return s.list(opt, &listOpt)
}
// ListUser lists all the user images.
func (s *ImagesServiceOp) ListUser(opt *ListOptions) ([]Image, *Response, error) {
listOpt := listImageOptions{Private: true}
return s.list(opt, &listOpt)
}
// GetByID retrieves an image by id.
func (s *ImagesServiceOp) GetByID(imageID int) (*Image, *Response, error) {
if imageID < 1 {
return nil, nil, NewArgError("imageID", "cannot be less than 1")
}
return s.get(interface{}(imageID))
}
// GetBySlug retrieves an image by slug.
func (s *ImagesServiceOp) GetBySlug(slug string) (*Image, *Response, error) {
if len(slug) < 1 {
return nil, nil, NewArgError("slug", "cannot be blank")
}
return s.get(interface{}(slug))
}
// Update an image name.
func (s *ImagesServiceOp) Update(imageID int, updateRequest *ImageUpdateRequest) (*Image, *Response, error) {
if imageID < 1 {
return nil, nil, NewArgError("imageID", "cannot be less than 1")
}
if updateRequest == nil {
return nil, nil, NewArgError("updateRequest", "cannot be nil")
}
path := fmt.Sprintf("%s/%d", imageBasePath, imageID)
req, err := s.client.NewRequest("PUT", path, updateRequest)
if err != nil {
return nil, nil, err
}
root := new(imageRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return &root.Image, resp, err
}
// Delete an image.
func (s *ImagesServiceOp) Delete(imageID int) (*Response, error) {
if imageID < 1 {
return nil, NewArgError("imageID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d", imageBasePath, imageID)
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 image
func (s *ImagesServiceOp) get(ID interface{}) (*Image, *Response, error) {
path := fmt.Sprintf("%s/%v", imageBasePath, ID)
req, err := s.client.NewRequest("GET", path, nil)
if err != nil {
return nil, nil, err
}
root := new(imageRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return &root.Image, resp, err
}
// Helper method for listing images
func (s *ImagesServiceOp) list(opt *ListOptions, listOpt *listImageOptions) ([]Image, *Response, error) {
path := imageBasePath
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 {

View File

@ -5,13 +5,15 @@ import "fmt"
const keysBasePath = "v2/account/keys"
// KeysService is an interface for interfacing with the keys
// endpoints of the Digital Ocean API
// See: https://developers.digitalocean.com/#keys
// endpoints of the DigitalOcean API
// See: https://developers.digitalocean.com/documentation/v2#keys
type KeysService interface {
List(*ListOptions) ([]Key, *Response, error)
GetByID(int) (*Key, *Response, error)
GetByFingerprint(string) (*Key, *Response, error)
Create(*KeyCreateRequest) (*Key, *Response, error)
UpdateByID(int, *KeyUpdateRequest) (*Key, *Response, error)
UpdateByFingerprint(string, *KeyUpdateRequest) (*Key, *Response, error)
DeleteByID(int) (*Response, error)
DeleteByFingerprint(string) (*Response, error)
}
@ -22,6 +24,8 @@ type KeysServiceOp struct {
client *Client
}
var _ KeysService = &KeysServiceOp{}
// Key represents a DigitalOcean Key.
type Key struct {
ID int `json:"id,float64,omitempty"`
@ -30,6 +34,11 @@ type Key struct {
PublicKey string `json:"public_key,omitempty"`
}
// KeyUpdateRequest represents a request to update a DigitalOcean key.
type KeyUpdateRequest struct {
Name string `json:"name"`
}
type keysRoot struct {
SSHKeys []Key `json:"ssh_keys"`
Links *Links `json:"links"`
@ -92,18 +101,30 @@ func (s *KeysServiceOp) get(path string) (*Key, *Response, error) {
// GetByID gets a Key by id
func (s *KeysServiceOp) GetByID(keyID int) (*Key, *Response, error) {
if keyID < 1 {
return nil, nil, NewArgError("keyID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d", keysBasePath, keyID)
return s.get(path)
}
// GetByFingerprint gets a Key by by fingerprint
func (s *KeysServiceOp) GetByFingerprint(fingerprint string) (*Key, *Response, error) {
if len(fingerprint) < 1 {
return nil, nil, NewArgError("fingerprint", "cannot not be empty")
}
path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint)
return s.get(path)
}
// Create a key using a KeyCreateRequest
func (s *KeysServiceOp) Create(createRequest *KeyCreateRequest) (*Key, *Response, error) {
if createRequest == nil {
return nil, nil, NewArgError("createRequest", "cannot be nil")
}
req, err := s.client.NewRequest("POST", keysBasePath, createRequest)
if err != nil {
return nil, nil, err
@ -118,6 +139,56 @@ func (s *KeysServiceOp) Create(createRequest *KeyCreateRequest) (*Key, *Response
return &root.SSHKey, resp, err
}
// UpdateByID updates a key name by ID.
func (s *KeysServiceOp) UpdateByID(keyID int, updateRequest *KeyUpdateRequest) (*Key, *Response, error) {
if keyID < 1 {
return nil, nil, NewArgError("keyID", "cannot be less than 1")
}
if updateRequest == nil {
return nil, nil, NewArgError("updateRequest", "cannot be nil")
}
path := fmt.Sprintf("%s/%d", keysBasePath, keyID)
req, err := s.client.NewRequest("PUT", path, updateRequest)
if err != nil {
return nil, nil, err
}
root := new(keyRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return &root.SSHKey, resp, err
}
// UpdateByFingerprint updates a key name by fingerprint.
func (s *KeysServiceOp) UpdateByFingerprint(fingerprint string, updateRequest *KeyUpdateRequest) (*Key, *Response, error) {
if len(fingerprint) < 1 {
return nil, nil, NewArgError("fingerprint", "cannot be empty")
}
if updateRequest == nil {
return nil, nil, NewArgError("updateRequest", "cannot be nil")
}
path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint)
req, err := s.client.NewRequest("PUT", path, updateRequest)
if err != nil {
return nil, nil, err
}
root := new(keyRoot)
resp, err := s.client.Do(req, root)
if err != nil {
return nil, resp, err
}
return &root.SSHKey, resp, err
}
// Delete key using a path
func (s *KeysServiceOp) delete(path string) (*Response, error) {
req, err := s.client.NewRequest("DELETE", path, nil)
@ -132,12 +203,20 @@ func (s *KeysServiceOp) delete(path string) (*Response, error) {
// DeleteByID deletes a key by its id
func (s *KeysServiceOp) DeleteByID(keyID int) (*Response, error) {
if keyID < 1 {
return nil, NewArgError("keyID", "cannot be less than 1")
}
path := fmt.Sprintf("%s/%d", keysBasePath, keyID)
return s.delete(path)
}
// DeleteByFingerprint deletes a key by its fingerprint
func (s *KeysServiceOp) DeleteByFingerprint(fingerprint string) (*Response, error) {
if len(fingerprint) < 1 {
return nil, NewArgError("fingerprint", "cannot be empty")
}
path := fmt.Sprintf("%s/%s", keysBasePath, fingerprint)
return s.delete(path)
}

View File

@ -80,6 +80,7 @@ func pageForURL(urlText string) (int, error) {
return page, nil
}
// Get a link action by id.
func (la *LinkAction) Get(client *Client) (*Action, *Response, error) {
return client.Actions.Get(la.ID)
}

View File

@ -1,8 +1,8 @@
package godo
// RegionsService is an interface for interfacing with the regions
// endpoints of the Digital Ocean API
// See: https://developers.digitalocean.com/#regions
// endpoints of the DigitalOcean API
// See: https://developers.digitalocean.com/documentation/v2#regions
type RegionsService interface {
List(*ListOptions) ([]Region, *Response, error)
}
@ -13,13 +13,15 @@ type RegionsServiceOp struct {
client *Client
}
var _ RegionsService = &RegionsServiceOp{}
// Region represents a DigitalOcean Region
type Region struct {
Slug string `json:"slug,omitempty"`
Name string `json:"name,omitempty"`
Sizes []string `json:"sizes,omitempty"`
Available bool `json:"available,omitempty`
Features []string `json:"features,omitempty`
Available bool `json:"available,omitempty"`
Features []string `json:"features,omitempty"`
}
type regionsRoot struct {

View File

@ -1,8 +1,8 @@
package godo
// SizesService is an interface for interfacing with the size
// endpoints of the Digital Ocean API
// See: https://developers.digitalocean.com/#sizes
// endpoints of the DigitalOcean API
// See: https://developers.digitalocean.com/documentation/v2#sizes
type SizesService interface {
List(*ListOptions) ([]Size, *Response, error)
}
@ -13,6 +13,8 @@ type SizesServiceOp struct {
client *Client
}
var _ SizesService = &SizesServiceOp{}
// Size represents a DigitalOcean Size
type Size struct {
Slug string `json:"slug,omitempty"`
@ -22,6 +24,8 @@ type Size struct {
PriceMonthly float64 `json:"price_monthly,omitempty"`
PriceHourly float64 `json:"price_hourly,omitempty"`
Regions []string `json:"regions,omitempty"`
Available bool `json:"available,omitempty"`
Transfer float64 `json:"transfer,omitempty"`
}
func (s Size) String() string {

View File

@ -3,13 +3,12 @@ package godo
import (
"bytes"
"fmt"
"io"
"reflect"
)
var timestampType = reflect.TypeOf(Timestamp{})
// Stringify attempts to create a string representation of Digital Ocean types
// Stringify attempts to create a string representation of DigitalOcean types
func Stringify(message interface{}) string {
var buf bytes.Buffer
v := reflect.ValueOf(message)
@ -18,9 +17,9 @@ func Stringify(message interface{}) string {
}
// stringifyValue was graciously cargoculted from the goprotubuf library
func stringifyValue(w io.Writer, val reflect.Value) {
func stringifyValue(w *bytes.Buffer, val reflect.Value) {
if val.Kind() == reflect.Ptr && val.IsNil() {
w.Write([]byte("<nil>"))
_, _ = w.Write([]byte("<nil>"))
return
}
@ -30,20 +29,20 @@ func stringifyValue(w io.Writer, val reflect.Value) {
case reflect.String:
fmt.Fprintf(w, `"%s"`, v)
case reflect.Slice:
w.Write([]byte{'['})
_, _ = w.Write([]byte{'['})
for i := 0; i < v.Len(); i++ {
if i > 0 {
w.Write([]byte{' '})
_, _ = w.Write([]byte{' '})
}
stringifyValue(w, v.Index(i))
}
w.Write([]byte{']'})
_, _ = w.Write([]byte{']'})
return
case reflect.Struct:
if v.Type().Name() != "" {
w.Write([]byte(v.Type().String()))
_, _ = w.Write([]byte(v.Type().String()))
}
// special handling of Timestamp values
@ -52,7 +51,7 @@ func stringifyValue(w io.Writer, val reflect.Value) {
return
}
w.Write([]byte{'{'})
_, _ = w.Write([]byte{'{'})
var sep bool
for i := 0; i < v.NumField(); i++ {
@ -65,17 +64,17 @@ func stringifyValue(w io.Writer, val reflect.Value) {
}
if sep {
w.Write([]byte(", "))
_, _ = w.Write([]byte(", "))
} else {
sep = true
}
w.Write([]byte(v.Type().Field(i).Name))
w.Write([]byte{':'})
_, _ = w.Write([]byte(v.Type().Field(i).Name))
_, _ = w.Write([]byte{':'})
stringifyValue(w, fv)
}
w.Write([]byte{'}'})
_, _ = w.Write([]byte{'}'})
default:
if v.CanInterface() {
fmt.Fprint(w, v.Interface())

View File

@ -18,7 +18,7 @@ func (t Timestamp) String() string {
// UnmarshalJSON implements the json.Unmarshaler interface.
// Time is expected in RFC3339 or Unix format.
func (t *Timestamp) UnmarshalJSON(data []byte) (err error) {
func (t *Timestamp) UnmarshalJSON(data []byte) error {
str := string(data)
i, err := strconv.ParseInt(str, 10, 64)
if err == nil {
@ -26,7 +26,7 @@ func (t *Timestamp) UnmarshalJSON(data []byte) (err error) {
} else {
t.Time, err = time.Parse(`"`+time.RFC3339+`"`, str)
}
return
return err
}
// Equal reports whether t and u are equal based on time.Equal

View File

@ -4,7 +4,7 @@ import (
"fmt"
"time"
"github.com/digitaloceancloud/godo"
"github.com/digitalocean/godo"
)
const (