Skip to content

Commit

Permalink
Portable Go Context Usage
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
akutz committed Apr 7, 2017
1 parent 4c04abe commit 8ed9e9e
Show file tree
Hide file tree
Showing 28 changed files with 310 additions and 114 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
language: go

go:
- 1.6.3
- 1.7
- tip
4 changes: 2 additions & 2 deletions account.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package godo

import "context"
import "github.com/digitalocean/godo/context"

// AccountService is an interface for interfacing with the Account
// endpoints of the DigitalOcean API
Expand Down Expand Up @@ -47,7 +47,7 @@ func (s *AccountServiceOp) Get(ctx context.Context) (*Account, *Response, error)
}

root := new(accountRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
Expand Down
7 changes: 4 additions & 3 deletions action.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package godo

import (
"context"
"fmt"

"github.com/digitalocean/godo/context"
)

const (
Expand Down Expand Up @@ -66,7 +67,7 @@ func (s *ActionsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Action
}

root := new(actionsRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
Expand All @@ -90,7 +91,7 @@ func (s *ActionsServiceOp) Get(ctx context.Context, id int) (*Action, *Response,
}

root := new(actionRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
Expand Down
11 changes: 6 additions & 5 deletions certificates.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package godo

import (
"context"
"path"

"github.com/digitalocean/godo/context"
)

const certificatesBasePath = "/v2/certificates"
Expand Down Expand Up @@ -59,7 +60,7 @@ func (c *CertificatesServiceOp) Get(ctx context.Context, cID string) (*Certifica
}

root := new(certificateRoot)
resp, err := c.client.Do(req, root)
resp, err := c.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
Expand All @@ -80,7 +81,7 @@ func (c *CertificatesServiceOp) List(ctx context.Context, opt *ListOptions) ([]C
}

root := new(certificatesRoot)
resp, err := c.client.Do(req, root)
resp, err := c.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
Expand All @@ -99,7 +100,7 @@ func (c *CertificatesServiceOp) Create(ctx context.Context, cr *CertificateReque
}

root := new(certificateRoot)
resp, err := c.client.Do(req, root)
resp, err := c.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
Expand All @@ -116,5 +117,5 @@ func (c *CertificatesServiceOp) Delete(ctx context.Context, cID string) (*Respon
return nil, err
}

return c.client.Do(req, nil)
return c.client.Do(ctx, req, nil)
}
98 changes: 98 additions & 0 deletions context/context.go
Original file line number Diff line number Diff line change
@@ -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 changes: 39 additions & 0 deletions context/context_go17.go
Original file line number Diff line number Diff line change
@@ -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()
}
41 changes: 41 additions & 0 deletions context/context_pre_go17.go
Original file line number Diff line number Diff line change
@@ -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()
}
21 changes: 11 additions & 10 deletions domains.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package godo

import (
"context"
"fmt"

"github.com/digitalocean/godo/context"
)

const domainsBasePath = "v2/domains"
Expand Down Expand Up @@ -104,7 +105,7 @@ func (s DomainsServiceOp) List(ctx context.Context, opt *ListOptions) ([]Domain,
}

root := new(domainsRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
Expand All @@ -129,7 +130,7 @@ func (s *DomainsServiceOp) Get(ctx context.Context, name string) (*Domain, *Resp
}

root := new(domainRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
Expand All @@ -151,7 +152,7 @@ func (s *DomainsServiceOp) Create(ctx context.Context, createRequest *DomainCrea
}

root := new(domainRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
Expand All @@ -171,7 +172,7 @@ func (s *DomainsServiceOp) Delete(ctx context.Context, name string) (*Response,
return nil, err
}

resp, err := s.client.Do(req, nil)
resp, err := s.client.Do(ctx, req, nil)

return resp, err
}
Expand Down Expand Up @@ -204,7 +205,7 @@ func (s *DomainsServiceOp) Records(ctx context.Context, domain string, opt *List
}

root := new(domainRecordsRoot)
resp, err := s.client.Do(req, root)
resp, err := s.client.Do(ctx, req, root)
if err != nil {
return nil, resp, err
}
Expand Down Expand Up @@ -233,7 +234,7 @@ func (s *DomainsServiceOp) Record(ctx context.Context, domain string, id int) (*
}

record := new(domainRecordRoot)
resp, err := s.client.Do(req, record)
resp, err := s.client.Do(ctx, req, record)
if err != nil {
return nil, resp, err
}
Expand All @@ -258,7 +259,7 @@ func (s *DomainsServiceOp) DeleteRecord(ctx context.Context, domain string, id i
return nil, err
}

resp, err := s.client.Do(req, nil)
resp, err := s.client.Do(ctx, req, nil)

return resp, err
}
Expand Down Expand Up @@ -289,7 +290,7 @@ func (s *DomainsServiceOp) EditRecord(ctx context.Context,
}

d := new(DomainRecord)
resp, err := s.client.Do(req, d)
resp, err := s.client.Do(ctx, req, d)
if err != nil {
return nil, resp, err
}
Expand Down Expand Up @@ -317,7 +318,7 @@ func (s *DomainsServiceOp) CreateRecord(ctx context.Context,
}

d := new(domainRecordRoot)
resp, err := s.client.Do(req, d)
resp, err := s.client.Do(ctx, req, d)
if err != nil {
return nil, resp, err
}
Expand Down
Loading

0 comments on commit 8ed9e9e

Please sign in to comment.