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,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). | ||||
|  |  | |||
|  | @ -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