Portable Go Context Usage

This patch updates the godo package to use a portable Go context -- a
Context that works with Go versions prior to Go 1.7 as well as Go 1.7
and onwards.
This commit is contained in:
akutz 2017-04-07 15:32:33 -05:00
parent 4c04abe183
commit 8ed9e9ea6c
28 changed files with 310 additions and 114 deletions

View File

@ -1,5 +1,6 @@
language: go language: go
go: go:
- 1.6.3
- 1.7 - 1.7
- tip - tip

View File

@ -1,6 +1,6 @@
package godo package godo
import "context" import "github.com/digitalocean/godo/context"
// AccountService is an interface for interfacing with the Account // AccountService is an interface for interfacing with the Account
// endpoints of the DigitalOcean API // endpoints of the DigitalOcean API
@ -47,7 +47,7 @@ func (s *AccountServiceOp) Get(ctx context.Context) (*Account, *Response, error)
} }
root := new(accountRoot) root := new(accountRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
const ( const (
@ -66,7 +67,7 @@ func (s *ActionsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Action
} }
root := new(actionsRoot) root := new(actionsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -90,7 +91,7 @@ func (s *ActionsServiceOp) Get(ctx context.Context, id int) (*Action, *Response,
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"path" "path"
"github.com/digitalocean/godo/context"
) )
const certificatesBasePath = "/v2/certificates" const certificatesBasePath = "/v2/certificates"
@ -59,7 +60,7 @@ func (c *CertificatesServiceOp) Get(ctx context.Context, cID string) (*Certifica
} }
root := new(certificateRoot) root := new(certificateRoot)
resp, err := c.client.Do(req, root) resp, err := c.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -80,7 +81,7 @@ func (c *CertificatesServiceOp) List(ctx context.Context, opt *ListOptions) ([]C
} }
root := new(certificatesRoot) root := new(certificatesRoot)
resp, err := c.client.Do(req, root) resp, err := c.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -99,7 +100,7 @@ func (c *CertificatesServiceOp) Create(ctx context.Context, cr *CertificateReque
} }
root := new(certificateRoot) root := new(certificateRoot)
resp, err := c.client.Do(req, root) resp, err := c.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -116,5 +117,5 @@ func (c *CertificatesServiceOp) Delete(ctx context.Context, cID string) (*Respon
return nil, err return nil, err
} }
return c.client.Do(req, nil) return c.client.Do(ctx, req, nil)
} }

98
context/context.go Normal file
View File

@ -0,0 +1,98 @@
package context
import "time"
// A Context carries a deadline, a cancelation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:s
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See http://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stores using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "golang.org/x/net/context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}

39
context/context_go17.go Normal file
View File

@ -0,0 +1,39 @@
// +build go1.7
package context
import (
"context"
"net/http"
)
// DoRequest submits an HTTP request.
func DoRequest(ctx Context, req *http.Request) (*http.Response, error) {
return DoRequestWithClient(ctx, http.DefaultClient, req)
}
// DoRequestWithClient submits an HTTP request using the specified client.
func DoRequestWithClient(
ctx Context,
client *http.Client,
req *http.Request) (*http.Response, error) {
req = req.WithContext(ctx)
return client.Do(req)
}
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter). TODO is recognized by static analysis tools that determine
// whether Contexts are propagated correctly in a program.
func TODO() Context {
return context.TODO()
}
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
return context.Background()
}

View File

@ -0,0 +1,41 @@
// +build !go1.7
package context
import (
"net/http"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
)
// DoRequest submits an HTTP request.
func DoRequest(ctx Context, req *http.Request) (*http.Response, error) {
return DoRequestWithClient(ctx, http.DefaultClient, req)
}
// DoRequestWithClient submits an HTTP request using the specified client.
func DoRequestWithClient(
ctx Context,
client *http.Client,
req *http.Request) (*http.Response, error) {
return ctxhttp.Do(ctx, client, req)
}
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter). TODO is recognized by static analysis tools that determine
// whether Contexts are propagated correctly in a program.
func TODO() Context {
return context.TODO()
}
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
return context.Background()
}

View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
const domainsBasePath = "v2/domains" const domainsBasePath = "v2/domains"
@ -104,7 +105,7 @@ func (s DomainsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Domain,
} }
root := new(domainsRoot) root := new(domainsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -129,7 +130,7 @@ func (s *DomainsServiceOp) Get(ctx context.Context, name string) (*Domain, *Resp
} }
root := new(domainRoot) root := new(domainRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -151,7 +152,7 @@ func (s *DomainsServiceOp) Create(ctx context.Context, createRequest *DomainCrea
} }
root := new(domainRoot) root := new(domainRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -171,7 +172,7 @@ func (s *DomainsServiceOp) Delete(ctx context.Context, name string) (*Response,
return nil, err return nil, err
} }
resp, err := s.client.Do(req, nil) resp, err := s.client.Do(ctx, req, nil)
return resp, err return resp, err
} }
@ -204,7 +205,7 @@ func (s *DomainsServiceOp) Records(ctx context.Context, domain string, opt *List
} }
root := new(domainRecordsRoot) root := new(domainRecordsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -233,7 +234,7 @@ func (s *DomainsServiceOp) Record(ctx context.Context, domain string, id int) (*
} }
record := new(domainRecordRoot) record := new(domainRecordRoot)
resp, err := s.client.Do(req, record) resp, err := s.client.Do(ctx, req, record)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -258,7 +259,7 @@ func (s *DomainsServiceOp) DeleteRecord(ctx context.Context, domain string, id i
return nil, err return nil, err
} }
resp, err := s.client.Do(req, nil) resp, err := s.client.Do(ctx, req, nil)
return resp, err return resp, err
} }
@ -289,7 +290,7 @@ func (s *DomainsServiceOp) EditRecord(ctx context.Context,
} }
d := new(DomainRecord) d := new(DomainRecord)
resp, err := s.client.Do(req, d) resp, err := s.client.Do(ctx, req, d)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -317,7 +318,7 @@ func (s *DomainsServiceOp) CreateRecord(ctx context.Context,
} }
d := new(domainRecordRoot) d := new(domainRecordRoot)
resp, err := s.client.Do(req, d) resp, err := s.client.Do(ctx, req, d)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -1,9 +1,10 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"net/url" "net/url"
"github.com/digitalocean/godo/context"
) )
// ActionRequest reprents DigitalOcean Action Request // ActionRequest reprents DigitalOcean Action Request
@ -252,7 +253,7 @@ func (s *DropletActionsServiceOp) doAction(ctx context.Context, id int, request
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -277,7 +278,7 @@ func (s *DropletActionsServiceOp) doActionByTag(ctx context.Context, tag string,
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -317,7 +318,7 @@ func (s *DropletActionsServiceOp) get(ctx context.Context, path string) (*Action
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -1,10 +1,11 @@
package godo package godo
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
const dropletBasePath = "v2/droplets" const dropletBasePath = "v2/droplets"
@ -280,7 +281,7 @@ func (s *DropletsServiceOp) list(ctx context.Context, path string) ([]Droplet, *
} }
root := new(dropletsRoot) root := new(dropletsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -327,7 +328,7 @@ func (s *DropletsServiceOp) Get(ctx context.Context, dropletID int) (*Droplet, *
} }
root := new(dropletRoot) root := new(dropletRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -349,7 +350,7 @@ func (s *DropletsServiceOp) Create(ctx context.Context, createRequest *DropletCr
} }
root := new(dropletRoot) root := new(dropletRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -374,7 +375,7 @@ func (s *DropletsServiceOp) CreateMultiple(ctx context.Context, createRequest *D
} }
root := new(dropletsRoot) root := new(dropletsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -392,7 +393,7 @@ func (s *DropletsServiceOp) delete(ctx context.Context, path string) (*Response,
return nil, err return nil, err
} }
resp, err := s.client.Do(req, nil) resp, err := s.client.Do(ctx, req, nil)
return resp, err return resp, err
} }
@ -437,7 +438,7 @@ func (s *DropletsServiceOp) Kernels(ctx context.Context, dropletID int, opt *Lis
} }
root := new(kernelsRoot) root := new(kernelsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if l := root.Links; l != nil { if l := root.Links; l != nil {
resp.Links = l resp.Links = l
} }
@ -463,7 +464,7 @@ func (s *DropletsServiceOp) Actions(ctx context.Context, dropletID int, opt *Lis
} }
root := new(actionsRoot) root := new(actionsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -492,7 +493,7 @@ func (s *DropletsServiceOp) Backups(ctx context.Context, dropletID int, opt *Lis
} }
root := new(backupsRoot) root := new(backupsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -521,7 +522,7 @@ func (s *DropletsServiceOp) Snapshots(ctx context.Context, dropletID int, opt *L
} }
root := new(dropletSnapshotsRoot) root := new(dropletSnapshotsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -546,7 +547,7 @@ func (s *DropletsServiceOp) Neighbors(ctx context.Context, dropletID int) ([]Dro
} }
root := new(dropletsRoot) root := new(dropletsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
const floatingBasePath = "v2/floating_ips" const floatingBasePath = "v2/floating_ips"
@ -68,7 +69,7 @@ func (f *FloatingIPsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Fl
} }
root := new(floatingIPsRoot) root := new(floatingIPsRoot)
resp, err := f.client.Do(req, root) resp, err := f.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -89,7 +90,7 @@ func (f *FloatingIPsServiceOp) Get(ctx context.Context, ip string) (*FloatingIP,
} }
root := new(floatingIPRoot) root := new(floatingIPRoot)
resp, err := f.client.Do(req, root) resp, err := f.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -108,7 +109,7 @@ func (f *FloatingIPsServiceOp) Create(ctx context.Context, createRequest *Floati
} }
root := new(floatingIPRoot) root := new(floatingIPRoot)
resp, err := f.client.Do(req, root) resp, err := f.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -128,7 +129,7 @@ func (f *FloatingIPsServiceOp) Delete(ctx context.Context, ip string) (*Response
return nil, err return nil, err
} }
resp, err := f.client.Do(req, nil) resp, err := f.client.Do(ctx, req, nil)
return resp, err return resp, err
} }

View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
// FloatingIPActionsService is an interface for interfacing with the // FloatingIPActionsService is an interface for interfacing with the
@ -62,7 +63,7 @@ func (s *FloatingIPActionsServiceOp) doAction(ctx context.Context, ip string, re
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -77,7 +78,7 @@ func (s *FloatingIPActionsServiceOp) get(ctx context.Context, path string) (*Act
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -92,7 +93,7 @@ func (s *FloatingIPActionsServiceOp) list(ctx context.Context, path string) ([]A
} }
root := new(actionsRoot) root := new(actionsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -2,7 +2,6 @@ package godo
import ( import (
"bytes" "bytes"
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@ -15,6 +14,8 @@ import (
"github.com/google/go-querystring/query" "github.com/google/go-querystring/query"
headerLink "github.com/tent/http-link-go" headerLink "github.com/tent/http-link-go"
"github.com/digitalocean/godo/context"
) )
const ( const (
@ -236,7 +237,6 @@ func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body int
return nil, err return nil, err
} }
req = req.WithContext(ctx)
req.Header.Add("Content-Type", mediaType) req.Header.Add("Content-Type", mediaType)
req.Header.Add("Accept", mediaType) req.Header.Add("Accept", mediaType)
req.Header.Add("User-Agent", c.UserAgent) req.Header.Add("User-Agent", c.UserAgent)
@ -293,8 +293,8 @@ func (r *Response) populateRate() {
// Do sends an API request and returns the API response. The API response is JSON decoded and stored in the value // Do sends an API request and returns the API response. The API response is JSON decoded and stored in the value
// pointed to by v, or returned as an error if an API error has occurred. If v implements the io.Writer interface, // pointed to by v, or returned as an error if an API error has occurred. If v implements the io.Writer interface,
// the raw response will be written to v, without attempting to decode it. // the raw response will be written to v, without attempting to decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) { func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) {
resp, err := c.client.Do(req) resp, err := context.DoRequestWithClient(ctx, c.client, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,7 +1,6 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -12,6 +11,8 @@ import (
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/digitalocean/godo/context"
) )
var ( var (
@ -224,7 +225,7 @@ func TestDo(t *testing.T) {
req, _ := client.NewRequest(ctx, "GET", "/", nil) req, _ := client.NewRequest(ctx, "GET", "/", nil)
body := new(foo) body := new(foo)
_, err := client.Do(req, body) _, err := client.Do(context.Background(), req, body)
if err != nil { if err != nil {
t.Fatalf("Do(): %v", err) t.Fatalf("Do(): %v", err)
} }
@ -244,7 +245,7 @@ func TestDo_httpError(t *testing.T) {
}) })
req, _ := client.NewRequest(ctx, "GET", "/", nil) req, _ := client.NewRequest(ctx, "GET", "/", nil)
_, err := client.Do(req, nil) _, err := client.Do(context.Background(), req, nil)
if err == nil { if err == nil {
t.Error("Expected HTTP 400 error.") t.Error("Expected HTTP 400 error.")
@ -262,7 +263,7 @@ func TestDo_redirectLoop(t *testing.T) {
}) })
req, _ := client.NewRequest(ctx, "GET", "/", nil) req, _ := client.NewRequest(ctx, "GET", "/", nil)
_, err := client.Do(req, nil) _, err := client.Do(context.Background(), req, nil)
if err == nil { if err == nil {
t.Error("Expected error to be returned.") t.Error("Expected error to be returned.")
@ -347,7 +348,7 @@ func TestDo_rateLimit(t *testing.T) {
} }
req, _ := client.NewRequest(ctx, "GET", "/", nil) req, _ := client.NewRequest(ctx, "GET", "/", nil)
_, err := client.Do(req, nil) _, err := client.Do(context.Background(), req, nil)
if err != nil { if err != nil {
t.Fatalf("Do(): %v", err) t.Fatalf("Do(): %v", err)
} }
@ -378,7 +379,7 @@ func TestDo_rateLimit_errorResponse(t *testing.T) {
var expected int var expected int
req, _ := client.NewRequest(ctx, "GET", "/", nil) req, _ := client.NewRequest(ctx, "GET", "/", nil)
_, _ = client.Do(req, nil) _, _ = client.Do(context.Background(), req, nil)
if expected = 60; client.Rate.Limit != expected { if expected = 60; client.Rate.Limit != expected {
t.Errorf("Client rate limit = %v, expected %v", client.Rate.Limit, expected) t.Errorf("Client rate limit = %v, expected %v", client.Rate.Limit, expected)
@ -431,7 +432,7 @@ func TestDo_completion_callback(t *testing.T) {
} }
completedResp = string(b) completedResp = string(b)
}) })
_, err := client.Do(req, body) _, err := client.Do(context.Background(), req, body)
if err != nil { if err != nil {
t.Fatalf("Do(): %v", err) t.Fatalf("Do(): %v", err)
} }

View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
// ImageActionsService is an interface for interfacing with the image actions // ImageActionsService is an interface for interfacing with the image actions
@ -40,7 +41,7 @@ func (i *ImageActionsServiceOp) Transfer(ctx context.Context, imageID int, trans
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := i.client.Do(req, root) resp, err := i.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -66,7 +67,7 @@ func (i *ImageActionsServiceOp) Convert(ctx context.Context, imageID int) (*Acti
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := i.client.Do(req, root) resp, err := i.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -92,7 +93,7 @@ func (i *ImageActionsServiceOp) Get(ctx context.Context, imageID, actionID int)
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := i.client.Do(req, root) resp, err := i.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
const imageBasePath = "v2/images" const imageBasePath = "v2/images"
@ -123,7 +124,7 @@ func (s *ImagesServiceOp) Update(ctx context.Context, imageID int, updateRequest
} }
root := new(imageRoot) root := new(imageRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -144,7 +145,7 @@ func (s *ImagesServiceOp) Delete(ctx context.Context, imageID int) (*Response, e
return nil, err return nil, err
} }
resp, err := s.client.Do(req, nil) resp, err := s.client.Do(ctx, req, nil)
return resp, err return resp, err
} }
@ -159,7 +160,7 @@ func (s *ImagesServiceOp) get(ctx context.Context, ID interface{}) (*Image, *Res
} }
root := new(imageRoot) root := new(imageRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -185,7 +186,7 @@ func (s *ImagesServiceOp) list(ctx context.Context, opt *ListOptions, listOpt *l
} }
root := new(imagesRoot) root := new(imagesRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

15
keys.go
View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
const keysBasePath = "v2/account/keys" const keysBasePath = "v2/account/keys"
@ -75,7 +76,7 @@ func (s *KeysServiceOp) List(ctx context.Context, opt *ListOptions) ([]Key, *Res
} }
root := new(keysRoot) root := new(keysRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -94,7 +95,7 @@ func (s *KeysServiceOp) get(ctx context.Context, path string) (*Key, *Response,
} }
root := new(keyRoot) root := new(keyRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -134,7 +135,7 @@ func (s *KeysServiceOp) Create(ctx context.Context, createRequest *KeyCreateRequ
} }
root := new(keyRoot) root := new(keyRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -159,7 +160,7 @@ func (s *KeysServiceOp) UpdateByID(ctx context.Context, keyID int, updateRequest
} }
root := new(keyRoot) root := new(keyRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -184,7 +185,7 @@ func (s *KeysServiceOp) UpdateByFingerprint(ctx context.Context, fingerprint str
} }
root := new(keyRoot) root := new(keyRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -199,7 +200,7 @@ func (s *KeysServiceOp) delete(ctx context.Context, path string) (*Response, err
return nil, err return nil, err
} }
resp, err := s.client.Do(req, nil) resp, err := s.client.Do(ctx, req, nil)
return resp, err return resp, err
} }

View File

@ -1,9 +1,10 @@
package godo package godo
import ( import (
"context"
"net/url" "net/url"
"strconv" "strconv"
"github.com/digitalocean/godo/context"
) )
// Links manages links that are returned along with a List // Links manages links that are returned along with a List

View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
const loadBalancersBasePath = "/v2/load_balancers" const loadBalancersBasePath = "/v2/load_balancers"
@ -148,7 +149,7 @@ func (l *LoadBalancersServiceOp) Get(ctx context.Context, lbID string) (*LoadBal
} }
root := new(loadBalancerRoot) root := new(loadBalancerRoot)
resp, err := l.client.Do(req, root) resp, err := l.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -169,7 +170,7 @@ func (l *LoadBalancersServiceOp) List(ctx context.Context, opt *ListOptions) ([]
} }
root := new(loadBalancersRoot) root := new(loadBalancersRoot)
resp, err := l.client.Do(req, root) resp, err := l.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -188,7 +189,7 @@ func (l *LoadBalancersServiceOp) Create(ctx context.Context, lbr *LoadBalancerRe
} }
root := new(loadBalancerRoot) root := new(loadBalancerRoot)
resp, err := l.client.Do(req, root) resp, err := l.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -206,7 +207,7 @@ func (l *LoadBalancersServiceOp) Update(ctx context.Context, lbID string, lbr *L
} }
root := new(loadBalancerRoot) root := new(loadBalancerRoot)
resp, err := l.client.Do(req, root) resp, err := l.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -223,7 +224,7 @@ func (l *LoadBalancersServiceOp) Delete(ctx context.Context, ldID string) (*Resp
return nil, err return nil, err
} }
return l.client.Do(req, nil) return l.client.Do(ctx, req, nil)
} }
// AddDroplets adds droplets to a load balancer. // AddDroplets adds droplets to a load balancer.
@ -235,7 +236,7 @@ func (l *LoadBalancersServiceOp) AddDroplets(ctx context.Context, lbID string, d
return nil, err return nil, err
} }
return l.client.Do(req, nil) return l.client.Do(ctx, req, nil)
} }
// RemoveDroplets removes droplets from a load balancer. // RemoveDroplets removes droplets from a load balancer.
@ -247,7 +248,7 @@ func (l *LoadBalancersServiceOp) RemoveDroplets(ctx context.Context, lbID string
return nil, err return nil, err
} }
return l.client.Do(req, nil) return l.client.Do(ctx, req, nil)
} }
// AddForwardingRules adds forwarding rules to a load balancer. // AddForwardingRules adds forwarding rules to a load balancer.
@ -259,7 +260,7 @@ func (l *LoadBalancersServiceOp) AddForwardingRules(ctx context.Context, lbID st
return nil, err return nil, err
} }
return l.client.Do(req, nil) return l.client.Do(ctx, req, nil)
} }
// RemoveForwardingRules removes forwarding rules from a load balancer. // RemoveForwardingRules removes forwarding rules from a load balancer.
@ -271,5 +272,5 @@ func (l *LoadBalancersServiceOp) RemoveForwardingRules(ctx context.Context, lbID
return nil, err return nil, err
} }
return l.client.Do(req, nil) return l.client.Do(ctx, req, nil)
} }

View File

@ -1,6 +1,6 @@
package godo package godo
import "context" import "github.com/digitalocean/godo/context"
// RegionsService is an interface for interfacing with the regions // RegionsService is an interface for interfacing with the regions
// endpoints of the DigitalOcean API // endpoints of the DigitalOcean API
@ -49,7 +49,7 @@ func (s *RegionsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Region
} }
root := new(regionsRoot) root := new(regionsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -1,6 +1,6 @@
package godo package godo
import "context" import "github.com/digitalocean/godo/context"
// SizesService is an interface for interfacing with the size // SizesService is an interface for interfacing with the size
// endpoints of the DigitalOcean API // endpoints of the DigitalOcean API
@ -53,7 +53,7 @@ func (s *SizesServiceOp) List(ctx context.Context, opt *ListOptions) ([]Size, *R
} }
root := new(sizesRoot) root := new(sizesRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
const snapshotBasePath = "v2/snapshots" const snapshotBasePath = "v2/snapshots"
@ -86,7 +87,7 @@ func (s *SnapshotsServiceOp) Delete(ctx context.Context, snapshotID string) (*Re
return nil, err return nil, err
} }
resp, err := s.client.Do(req, nil) resp, err := s.client.Do(ctx, req, nil)
return resp, err return resp, err
} }
@ -101,7 +102,7 @@ func (s *SnapshotsServiceOp) get(ctx context.Context, ID string) (*Snapshot, *Re
} }
root := new(snapshotRoot) root := new(snapshotRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -127,7 +128,7 @@ func (s *SnapshotsServiceOp) list(ctx context.Context, opt *ListOptions, listOpt
} }
root := new(snapshotsRoot) root := new(snapshotsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@ -1,11 +1,12 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"net/http" "net/http"
"reflect" "reflect"
"testing" "testing"
"github.com/digitalocean/godo/context"
) )
func TestSnapshots_List(t *testing.T) { func TestSnapshots_List(t *testing.T) {

View File

@ -1,9 +1,10 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"time" "time"
"github.com/digitalocean/godo/context"
) )
const ( const (
@ -99,7 +100,7 @@ func (svc *StorageServiceOp) ListVolumes(ctx context.Context, params *ListVolume
} }
root := new(storageVolumesRoot) root := new(storageVolumesRoot)
resp, err := svc.client.Do(req, root) resp, err := svc.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -121,7 +122,7 @@ func (svc *StorageServiceOp) CreateVolume(ctx context.Context, createRequest *Vo
} }
root := new(storageVolumeRoot) root := new(storageVolumeRoot)
resp, err := svc.client.Do(req, root) resp, err := svc.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -138,7 +139,7 @@ func (svc *StorageServiceOp) GetVolume(ctx context.Context, id string) (*Volume,
} }
root := new(storageVolumeRoot) root := new(storageVolumeRoot)
resp, err := svc.client.Do(req, root) resp, err := svc.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -154,7 +155,7 @@ func (svc *StorageServiceOp) DeleteVolume(ctx context.Context, id string) (*Resp
if err != nil { if err != nil {
return nil, err return nil, err
} }
return svc.client.Do(req, nil) return svc.client.Do(ctx, req, nil)
} }
// SnapshotCreateRequest represents a request to create a block store // SnapshotCreateRequest represents a request to create a block store
@ -179,7 +180,7 @@ func (svc *StorageServiceOp) ListSnapshots(ctx context.Context, volumeID string,
} }
root := new(snapshotsRoot) root := new(snapshotsRoot)
resp, err := svc.client.Do(req, root) resp, err := svc.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -201,7 +202,7 @@ func (svc *StorageServiceOp) CreateSnapshot(ctx context.Context, createRequest *
} }
root := new(snapshotRoot) root := new(snapshotRoot)
resp, err := svc.client.Do(req, root) resp, err := svc.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -218,7 +219,7 @@ func (svc *StorageServiceOp) GetSnapshot(ctx context.Context, id string) (*Snaps
} }
root := new(snapshotRoot) root := new(snapshotRoot)
resp, err := svc.client.Do(req, root) resp, err := svc.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -234,5 +235,5 @@ func (svc *StorageServiceOp) DeleteSnapshot(ctx context.Context, id string) (*Re
if err != nil { if err != nil {
return nil, err return nil, err
} }
return svc.client.Do(req, nil) return svc.client.Do(ctx, req, nil)
} }

View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
// StorageActionsService is an interface for interfacing with the // StorageActionsService is an interface for interfacing with the
@ -82,7 +83,7 @@ func (s *StorageActionsServiceOp) doAction(ctx context.Context, volumeID string,
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -97,7 +98,7 @@ func (s *StorageActionsServiceOp) get(ctx context.Context, path string) (*Action
} }
root := new(actionRoot) root := new(actionRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -112,7 +113,7 @@ func (s *StorageActionsServiceOp) list(ctx context.Context, path string) ([]Acti
} }
root := new(actionsRoot) root := new(actionsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

15
tags.go
View File

@ -1,8 +1,9 @@
package godo package godo
import ( import (
"context"
"fmt" "fmt"
"github.com/digitalocean/godo/context"
) )
const tagsBasePath = "v2/tags" const tagsBasePath = "v2/tags"
@ -98,7 +99,7 @@ func (s *TagsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Tag, *Res
} }
root := new(tagsRoot) root := new(tagsRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -119,7 +120,7 @@ func (s *TagsServiceOp) Get(ctx context.Context, name string) (*Tag, *Response,
} }
root := new(tagRoot) root := new(tagRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -139,7 +140,7 @@ func (s *TagsServiceOp) Create(ctx context.Context, createRequest *TagCreateRequ
} }
root := new(tagRoot) root := new(tagRoot)
resp, err := s.client.Do(req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
@ -159,7 +160,7 @@ func (s *TagsServiceOp) Delete(ctx context.Context, name string) (*Response, err
return nil, err return nil, err
} }
resp, err := s.client.Do(req, nil) resp, err := s.client.Do(ctx, req, nil)
return resp, err return resp, err
} }
@ -180,7 +181,7 @@ func (s *TagsServiceOp) TagResources(ctx context.Context, name string, tagReques
return nil, err return nil, err
} }
resp, err := s.client.Do(req, nil) resp, err := s.client.Do(ctx, req, nil)
return resp, err return resp, err
} }
@ -201,7 +202,7 @@ func (s *TagsServiceOp) UntagResources(ctx context.Context, name string, untagRe
return nil, err return nil, err
} }
resp, err := s.client.Do(req, nil) resp, err := s.client.Do(ctx, req, nil)
return resp, err return resp, err
} }

View File

@ -1,11 +1,11 @@
package util package util
import ( import (
"context"
"fmt" "fmt"
"time" "time"
"github.com/digitalocean/godo" "github.com/digitalocean/godo"
"github.com/digitalocean/godo/context"
) )
const ( const (

View File

@ -1,11 +1,10 @@
package util package util
import ( import (
"context"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"github.com/digitalocean/godo" "github.com/digitalocean/godo"
"github.com/digitalocean/godo/context"
) )
func ExampleWaitForActive() { func ExampleWaitForActive() {