mirror of https://github.com/docker/docs.git
Update digital ocean dependencies
Signed-off-by: David Gageot <david@gageot.net>
This commit is contained in:
parent
480051c1f7
commit
198ab07be0
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
||||
- tip
|
||||
|
|
@ -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 .
|
||||
```
|
||||
|
|
@ -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 ./...
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
[](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,7 +87,7 @@ 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
|
||||
|
|
@ -80,21 +95,42 @@ func DropletList(client *godo.Client) ([]godo.Droplet, error) {
|
|||
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).
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/digitaloceancloud/godo"
|
||||
"github.com/digitalocean/godo"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
|||
Loading…
Reference in New Issue