depencies: update `go-tfe`

This commit is contained in:
Sander van Harmelen 2018-11-13 11:17:39 +01:00
parent 8c54da0ad2
commit d0c320f148
37 changed files with 1464 additions and 257 deletions

10
go.mod
View File

@ -45,7 +45,6 @@ require (
github.com/golang/protobuf v1.2.0
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c // indirect
github.com/google/go-cmp v0.2.0
github.com/google/go-querystring v1.0.0 // indirect
github.com/googleapis/gax-go v0.0.0-20161107002406-da06d194a00e // indirect
github.com/gophercloud/gophercloud v0.0.0-20170524130959-3027adb1ce72
github.com/gopherjs/gopherjs v0.0.0-20181004151105-1babbf986f6f // indirect
@ -56,19 +55,18 @@ require (
github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089
github.com/hashicorp/errwrap v1.0.0
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
github.com/hashicorp/go-cleanhttp v0.0.0-20171130225243-06c9ea3a335b
github.com/hashicorp/go-cleanhttp v0.5.0
github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f
github.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa // indirect
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c // indirect
github.com/hashicorp/go-multierror v1.0.0
github.com/hashicorp/go-plugin v0.0.0-20181002195811-1faddcf740b6
github.com/hashicorp/go-retryablehttp v0.0.0-20160930035102-6e85be8fee1d
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc // indirect
github.com/hashicorp/go-slug v0.1.0 // indirect
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect
github.com/hashicorp/go-tfe v0.2.6
github.com/hashicorp/go-tfe v0.2.9
github.com/hashicorp/go-uuid v1.0.0
github.com/hashicorp/go-version v0.0.0-20180322230233-23480c066577
github.com/hashicorp/golang-lru v0.5.0 // indirect
@ -121,7 +119,6 @@ require (
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
github.com/soheilhy/cmux v0.1.4 // indirect
github.com/spf13/afero v1.0.2
github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d // indirect
github.com/terraform-providers/terraform-provider-aws v1.41.0
github.com/terraform-providers/terraform-provider-openstack v0.0.0-20170616075611-4080a521c6ea
github.com/terraform-providers/terraform-provider-template v1.0.0 // indirect
@ -142,7 +139,6 @@ require (
golang.org/x/net v0.0.0-20181017193950-04a2e542c03f
golang.org/x/oauth2 v0.0.0-20181003184128-c57b0facaced
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e // indirect
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 // indirect
google.golang.org/api v0.0.0-20181015145326-625cd1887957
google.golang.org/appengine v1.2.0 // indirect
google.golang.org/grpc v1.14.0

18
go.sum
View File

@ -121,8 +121,8 @@ github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/U
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8=
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4=
github.com/hashicorp/go-cleanhttp v0.0.0-20171130225243-06c9ea3a335b h1:xrvnoavY7pMnMB/4x+cSAMgkzwjiSyilS55LZ14Ko7o=
github.com/hashicorp/go-cleanhttp v0.0.0-20171130225243-06c9ea3a335b/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86 h1:mv3oKLM8sTaxmU/PrT39T35HRnUfchK+vtzXw6Ci9lY=
github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86/go.mod h1:6rdJFnhkXnzGOJbvkrdv4t9nLwKcVA+tmbQeUlkIzrU=
github.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f h1:Yv9YzBlAETjy6AOX9eLBZ3nshNVRREgerT/3nvxlGho=
@ -137,8 +137,8 @@ github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uP
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-plugin v0.0.0-20181002195811-1faddcf740b6 h1:czAJ5CXRPr+6vd6RGdJelApnxNbK3dAkakgBwLEWfrc=
github.com/hashicorp/go-plugin v0.0.0-20181002195811-1faddcf740b6/go.mod h1:JSqWYsict+jzcj0+xElxyrBQRPNoiWQuddnxArJ7XHQ=
github.com/hashicorp/go-retryablehttp v0.0.0-20160930035102-6e85be8fee1d h1:/T1aqTlRV/71ER/wHvhqTZaXGQW7XSO+F16mIIHw7zc=
github.com/hashicorp/go-retryablehttp v0.0.0-20160930035102-6e85be8fee1d/go.mod h1:fXcdFsQoipQa7mwORhKad5jmDCeSy/RCGzWA08PO0lM=
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6 h1:qCv4319q2q7XKn0MQbi8p37hsJ+9Xo8e6yojA73JVxk=
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6/go.mod h1:fXcdFsQoipQa7mwORhKad5jmDCeSy/RCGzWA08PO0lM=
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:9HVkPxOpo+yO93Ah4yrO67d/qh0fbLLWbKqhYjyHq9A=
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg=
github.com/hashicorp/go-safetemp v0.0.0-20180326211150-b1a1dbde6fdc h1:wAa9fGALVHfjYxZuXRnmuJG2CnwRpJYOTvY6YdErAh0=
@ -147,8 +147,10 @@ github.com/hashicorp/go-slug v0.1.0 h1:MJGEiOwRGrQCBmMMZABHqIESySFJ4ajrsjgDI4/aF
github.com/hashicorp/go-slug v0.1.0/go.mod h1:+zDycQOzGqOqMW7Kn2fp9vz/NtqpMLQlgb9JUF+0km4=
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM=
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-tfe v0.2.6 h1:o2ryV7ZS0BgaLfNvzWz+A/6J70UETMy+wFL+DQlUy/M=
github.com/hashicorp/go-tfe v0.2.6/go.mod h1:nJs7lSMcNPGQQtjyPG6en099CQ/f83+hfeeSqehl2Fg=
github.com/hashicorp/go-tfe v0.2.7 h1:Cy0irO9Qfgdn7FmvxSoXIQrRa3iM/kFmp/c0oCboCow=
github.com/hashicorp/go-tfe v0.2.7/go.mod h1:WJgjAJVdnXYPOWF6j66VI20djUGfeFjeayIgUDhohsU=
github.com/hashicorp/go-tfe v0.2.9 h1:CmxjF5zBKh5XBf2fMseJPaSKxKIauIIS4r+6+hNX8JM=
github.com/hashicorp/go-tfe v0.2.9/go.mod h1:WJgjAJVdnXYPOWF6j66VI20djUGfeFjeayIgUDhohsU=
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v0.0.0-20180322230233-23480c066577 h1:at4+18LrM8myamuV7/vT6x2s1JNXp2k4PsSbt4I02X4=
@ -343,8 +345,8 @@ golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e h1:LSlw/Dbj0MkNvPYAAkGinYmGl
golang.org/x/sys v0.0.0-20180925112736-b09afc3d579e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181015145326-625cd1887957 h1:jwCmWUTrTFfjsobRuGurnCQeW4NZKijaIf6yAXwLR0E=
google.golang.org/api v0.0.0-20181015145326-625cd1887957/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=

1
vendor/github.com/hashicorp/go-cleanhttp/go.mod generated vendored Normal file
View File

@ -0,0 +1 @@
module github.com/hashicorp/go-cleanhttp

43
vendor/github.com/hashicorp/go-cleanhttp/handlers.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
package cleanhttp
import (
"net/http"
"strings"
"unicode"
)
// HandlerInput provides input options to cleanhttp's handlers
type HandlerInput struct {
ErrStatus int
}
// PrintablePathCheckHandler is a middleware that ensures the request path
// contains only printable runes.
func PrintablePathCheckHandler(next http.Handler, input *HandlerInput) http.Handler {
// Nil-check on input to make it optional
if input == nil {
input = &HandlerInput{
ErrStatus: http.StatusBadRequest,
}
}
// Default to http.StatusBadRequest on error
if input.ErrStatus == 0 {
input.ErrStatus = http.StatusBadRequest
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check URL path for non-printable characters
idx := strings.IndexFunc(r.URL.Path, func(c rune) bool {
return !unicode.IsPrint(c)
})
if idx != -1 {
w.WriteHeader(input.ErrStatus)
return
}
next.ServeHTTP(w, r)
return
})
}

View File

@ -3,7 +3,7 @@ sudo: false
language: go
go:
- 1.6.3
- 1.8.1
branches:
only:

View File

@ -14,13 +14,16 @@ makes `retryablehttp` very easy to drop into existing programs.
`retryablehttp` performs automatic retries under certain conditions. Mainly, if
an error is returned by the client (connection errors, etc.), or if a 500-range
response code is received, then a retry is invoked after a wait period.
Otherwise, the response is returned and left to the caller to interpret.
response code is received (except 501), then a retry is invoked after a wait
period. Otherwise, the response is returned and left to the caller to
interpret.
The main difference from `net/http` is that requests which take a request body
(POST/PUT et. al) require an `io.ReadSeeker` to be provided. This enables the
request body to be "rewound" if the initial request fails so that the full
request can be attempted again.
(POST/PUT et. al) can have the body provided in a number of ways (some more or
less efficient) that allow "rewinding" the request body if the initial request
fails so that the full request can be attempted again. See the
[godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp) for more
details.
Example Use
===========

View File

@ -8,18 +8,28 @@
// response is received, then a retry is invoked. Otherwise, the response is
// returned and left to the caller to interpret.
//
// The main difference from net/http is that requests which take a request body
// (POST/PUT et. al) require an io.ReadSeeker to be provided. This enables the
// request body to be "rewound" if the initial request fails so that the full
// request can be attempted again.
// Requests which take a request body should provide a non-nil function
// parameter. The best choice is to provide either a function satisfying
// ReaderFunc which provides multiple io.Readers in an efficient manner, a
// *bytes.Buffer (the underlying raw byte slice will be used) or a raw byte
// slice. As it is a reference type, and we will wrap it as needed by readers,
// we can efficiently re-use the request body without needing to copy it. If an
// io.Reader (such as a *bytes.Reader) is provided, the full body will be read
// prior to the first request, and will be efficiently re-used for any retries.
// ReadSeeker can be used, but some users have observed occasional data races
// between the net/http library and the Seek functionality of some
// implementations of ReadSeeker, so should be avoided if possible.
package retryablehttp
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"log"
"math"
"math/rand"
"net/http"
"net/url"
"os"
@ -44,6 +54,9 @@ var (
respReadLimit = int64(4096)
)
// ReaderFunc is the type of function that can be given natively to NewRequest
type ReaderFunc func() (io.Reader, error)
// LenReader is an interface implemented by many in-memory io.Reader's. Used
// for automatically sending the right Content-Length header when possible.
type LenReader interface {
@ -54,32 +67,118 @@ type LenReader interface {
type Request struct {
// body is a seekable reader over the request body payload. This is
// used to rewind the request data in between retries.
body io.ReadSeeker
body ReaderFunc
// Embed an HTTP request directly. This makes a *Request act exactly
// like an *http.Request so that all meta methods are supported.
*http.Request
}
// WithContext returns wrapped Request with a shallow copy of underlying *http.Request
// with its context changed to ctx. The provided ctx must be non-nil.
func (r *Request) WithContext(ctx context.Context) *Request {
r.Request = r.Request.WithContext(ctx)
return r
}
// NewRequest creates a new wrapped request.
func NewRequest(method, url string, body io.ReadSeeker) (*Request, error) {
// Wrap the body in a noop ReadCloser if non-nil. This prevents the
// reader from being closed by the HTTP client.
var rcBody io.ReadCloser
if body != nil {
rcBody = ioutil.NopCloser(body)
func NewRequest(method, url string, rawBody interface{}) (*Request, error) {
var err error
var body ReaderFunc
var contentLength int64
if rawBody != nil {
switch rawBody.(type) {
// If they gave us a function already, great! Use it.
case ReaderFunc:
body = rawBody.(ReaderFunc)
tmp, err := body()
if err != nil {
return nil, err
}
if lr, ok := tmp.(LenReader); ok {
contentLength = int64(lr.Len())
}
if c, ok := tmp.(io.Closer); ok {
c.Close()
}
case func() (io.Reader, error):
body = rawBody.(func() (io.Reader, error))
tmp, err := body()
if err != nil {
return nil, err
}
if lr, ok := tmp.(LenReader); ok {
contentLength = int64(lr.Len())
}
if c, ok := tmp.(io.Closer); ok {
c.Close()
}
// If a regular byte slice, we can read it over and over via new
// readers
case []byte:
buf := rawBody.([]byte)
body = func() (io.Reader, error) {
return bytes.NewReader(buf), nil
}
contentLength = int64(len(buf))
// If a bytes.Buffer we can read the underlying byte slice over and
// over
case *bytes.Buffer:
buf := rawBody.(*bytes.Buffer)
body = func() (io.Reader, error) {
return bytes.NewReader(buf.Bytes()), nil
}
contentLength = int64(buf.Len())
// We prioritize *bytes.Reader here because we don't really want to
// deal with it seeking so want it to match here instead of the
// io.ReadSeeker case.
case *bytes.Reader:
buf, err := ioutil.ReadAll(rawBody.(*bytes.Reader))
if err != nil {
return nil, err
}
body = func() (io.Reader, error) {
return bytes.NewReader(buf), nil
}
contentLength = int64(len(buf))
// Compat case
case io.ReadSeeker:
raw := rawBody.(io.ReadSeeker)
body = func() (io.Reader, error) {
raw.Seek(0, 0)
return ioutil.NopCloser(raw), nil
}
if lr, ok := raw.(LenReader); ok {
contentLength = int64(lr.Len())
}
// Read all in so we can reset
case io.Reader:
buf, err := ioutil.ReadAll(rawBody.(io.Reader))
if err != nil {
return nil, err
}
body = func() (io.Reader, error) {
return bytes.NewReader(buf), nil
}
contentLength = int64(len(buf))
default:
return nil, fmt.Errorf("cannot handle type %T", rawBody)
}
}
// Make the request with the noop-closer for the body.
httpReq, err := http.NewRequest(method, url, rcBody)
httpReq, err := http.NewRequest(method, url, nil)
if err != nil {
return nil, err
}
// Check if we can set the Content-Length automatically.
if lr, ok := body.(LenReader); ok {
httpReq.ContentLength = int64(lr.Len())
}
httpReq.ContentLength = contentLength
return &Request{body, httpReq}, nil
}
@ -105,7 +204,18 @@ type ResponseLogHook func(*log.Logger, *http.Response)
// Client will close any response body when retrying, but if the retry is
// aborted it is up to the CheckResponse callback to properly close any
// response body before returning.
type CheckRetry func(resp *http.Response, err error) (bool, error)
type CheckRetry func(ctx context.Context, resp *http.Response, err error) (bool, error)
// Backoff specifies a policy for how long to wait between retries.
// It is called after a failing request to determine the amount of time
// that should pass before trying again.
type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration
// ErrorHandler is called if retries are expired, containing the last status
// from the http library. If not specified, default behavior for the library is
// to close the body and return an error indicating how many tries were
// attempted. If overriding this, be sure to close the body if needed.
type ErrorHandler func(resp *http.Response, err error, numTries int) (*http.Response, error)
// Client is used to make HTTP requests. It adds additional functionality
// like automatic retries to tolerate minor outages.
@ -128,6 +238,12 @@ type Client struct {
// CheckRetry specifies the policy for handling retries, and is called
// after each request. The default policy is DefaultRetryPolicy.
CheckRetry CheckRetry
// Backoff specifies the policy for how long to wait between retries
Backoff Backoff
// ErrorHandler specifies the custom error handler to use, if any
ErrorHandler ErrorHandler
}
// NewClient creates a new Client with default settings.
@ -139,12 +255,18 @@ func NewClient() *Client {
RetryWaitMax: defaultRetryWaitMax,
RetryMax: defaultRetryMax,
CheckRetry: DefaultRetryPolicy,
Backoff: DefaultBackoff,
}
}
// DefaultRetryPolicy provides a default callback for Client.CheckRetry, which
// will retry on connection errors and server errors.
func DefaultRetryPolicy(resp *http.Response, err error) (bool, error) {
func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) {
// do not retry on context.Canceled or context.DeadlineExceeded
if ctx.Err() != nil {
return false, ctx.Err()
}
if err != nil {
return true, err
}
@ -152,24 +274,92 @@ func DefaultRetryPolicy(resp *http.Response, err error) (bool, error) {
// the server time to recover, as 500's are typically not permanent
// errors and may relate to outages on the server side. This will catch
// invalid response codes as well, like 0 and 999.
if resp.StatusCode == 0 || resp.StatusCode >= 500 {
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) {
return true, nil
}
return false, nil
}
// DefaultBackoff provides a default callback for Client.Backoff which
// will perform exponential backoff based on the attempt number and limited
// by the provided minimum and maximum durations.
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
mult := math.Pow(2, float64(attemptNum)) * float64(min)
sleep := time.Duration(mult)
if float64(sleep) != mult || sleep > max {
sleep = max
}
return sleep
}
// LinearJitterBackoff provides a callback for Client.Backoff which will
// perform linear backoff based on the attempt number and with jitter to
// prevent a thundering herd.
//
// min and max here are *not* absolute values. The number to be multipled by
// the attempt number will be chosen at random from between them, thus they are
// bounding the jitter.
//
// For instance:
// * To get strictly linear backoff of one second increasing each retry, set
// both to one second (1s, 2s, 3s, 4s, ...)
// * To get a small amount of jitter centered around one second increasing each
// retry, set to around one second, such as a min of 800ms and max of 1200ms
// (892ms, 2102ms, 2945ms, 4312ms, ...)
// * To get extreme jitter, set to a very wide spread, such as a min of 100ms
// and a max of 20s (15382ms, 292ms, 51321ms, 35234ms, ...)
func LinearJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
// attemptNum always starts at zero but we want to start at 1 for multiplication
attemptNum++
if max <= min {
// Unclear what to do here, or they are the same, so return min *
// attemptNum
return min * time.Duration(attemptNum)
}
// Seed rand; doing this every time is fine
rand := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
// Pick a random number that lies somewhere between the min and max and
// multiply by the attemptNum. attemptNum starts at zero so we always
// increment here. We first get a random percentage, then apply that to the
// difference between min and max, and add to min.
jitter := rand.Float64() * float64(max-min)
jitterMin := int64(jitter) + int64(min)
return time.Duration(jitterMin * int64(attemptNum))
}
// PassthroughErrorHandler is an ErrorHandler that directly passes through the
// values from the net/http library for the final request. The body is not
// closed.
func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Response, error) {
return resp, err
}
// Do wraps calling an HTTP method with retries.
func (c *Client) Do(req *Request) (*http.Response, error) {
c.Logger.Printf("[DEBUG] %s %s", req.Method, req.URL)
if c.Logger != nil {
c.Logger.Printf("[DEBUG] %s %s", req.Method, req.URL)
}
var resp *http.Response
var err error
for i := 0; ; i++ {
var code int // HTTP response code
// Always rewind the request body when non-nil.
if req.body != nil {
if _, err := req.body.Seek(0, 0); err != nil {
return nil, fmt.Errorf("failed to seek body: %v", err)
body, err := req.body()
if err != nil {
return resp, err
}
if c, ok := body.(io.ReadCloser); ok {
req.Request.Body = c
} else {
req.Request.Body = ioutil.NopCloser(body)
}
}
@ -178,13 +368,18 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
}
// Attempt the request
resp, err := c.HTTPClient.Do(req.Request)
resp, err = c.HTTPClient.Do(req.Request)
if resp != nil {
code = resp.StatusCode
}
// Check if we should continue with retries.
checkOK, checkErr := c.CheckRetry(resp, err)
checkOK, checkErr := c.CheckRetry(req.Request.Context(), resp, err)
if err != nil {
c.Logger.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err)
if c.Logger != nil {
c.Logger.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err)
}
} else {
// Call this here to maintain the behavior of logging all requests,
// even if CheckRetry signals to stop.
@ -202,25 +397,38 @@ func (c *Client) Do(req *Request) (*http.Response, error) {
return resp, err
}
// We do this before drainBody beause there's no need for the I/O if
// we're breaking out
remain := c.RetryMax - i
if remain <= 0 {
break
}
// We're going to retry, consume any response to reuse the connection.
if err == nil {
if err == nil && resp != nil {
c.drainBody(resp.Body)
}
remain := c.RetryMax - i
if remain == 0 {
break
}
wait := backoff(c.RetryWaitMin, c.RetryWaitMax, i)
wait := c.Backoff(c.RetryWaitMin, c.RetryWaitMax, i, resp)
desc := fmt.Sprintf("%s %s", req.Method, req.URL)
if code > 0 {
desc = fmt.Sprintf("%s (status: %d)", desc, code)
}
c.Logger.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
if c.Logger != nil {
c.Logger.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
}
time.Sleep(wait)
}
// Return an error if we fall out of the retry loop
if c.ErrorHandler != nil {
return c.ErrorHandler(resp, err, c.RetryMax+1)
}
// By default, we close the response body and return an error without
// returning the response
if resp != nil {
resp.Body.Close()
}
return nil, fmt.Errorf("%s %s giving up after %d attempts",
req.Method, req.URL, c.RetryMax+1)
}
@ -230,7 +438,9 @@ func (c *Client) drainBody(body io.ReadCloser) {
defer body.Close()
_, err := io.Copy(ioutil.Discard, io.LimitReader(body, respReadLimit))
if err != nil {
c.Logger.Printf("[ERR] error reading response body: %v", err)
if c.Logger != nil {
c.Logger.Printf("[ERR] error reading response body: %v", err)
}
}
}
@ -263,12 +473,12 @@ func (c *Client) Head(url string) (*http.Response, error) {
}
// Post is a shortcut for doing a POST request without making a new client.
func Post(url, bodyType string, body io.ReadSeeker) (*http.Response, error) {
func Post(url, bodyType string, body interface{}) (*http.Response, error) {
return defaultClient.Post(url, bodyType, body)
}
// Post is a convenience method for doing simple POST requests.
func (c *Client) Post(url, bodyType string, body io.ReadSeeker) (*http.Response, error) {
func (c *Client) Post(url, bodyType string, body interface{}) (*http.Response, error) {
req, err := NewRequest("POST", url, body)
if err != nil {
return nil, err
@ -288,15 +498,3 @@ func PostForm(url string, data url.Values) (*http.Response, error) {
func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) {
return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
// backoff is used to calculate how long to sleep before retrying
// after observing failures. It takes the minimum/maximum wait time and
// iteration, and returns the duration to wait.
func backoff(min, max time.Duration, iter int) time.Duration {
mult := math.Pow(2, float64(iter)) * float64(min)
sleep := time.Duration(mult)
if float64(sleep) != mult || sleep > max {
sleep = max
}
return sleep
}

View File

@ -28,6 +28,7 @@ Currently the following endpoints are supported:
- [x] [Organizations](https://www.terraform.io/docs/enterprise/api/organizations.html)
- [x] [Organization Tokens](https://www.terraform.io/docs/enterprise/api/organization-tokens.html)
- [x] [Policies](https://www.terraform.io/docs/enterprise/api/policies.html)
- [x] [Policy Sets](https://www.terraform.io/docs/enterprise/api/policy-sets.html)
- [x] [Policy Checks](https://www.terraform.io/docs/enterprise/api/policy-checks.html)
- [ ] [Registry Modules](https://www.terraform.io/docs/enterprise/api/modules.html)
- [x] [Runs](https://www.terraform.io/docs/enterprise/api/run.html)

View File

@ -69,7 +69,7 @@ type ApplyStatusTimestamps struct {
// Read an apply by its ID.
func (s *applies) Read(ctx context.Context, applyID string) (*Apply, error) {
if !validStringID(&applyID) {
return nil, errors.New("Invalid value for apply ID")
return nil, errors.New("invalid value for apply ID")
}
u := fmt.Sprintf("applies/%s", url.QueryEscape(applyID))
@ -90,7 +90,7 @@ func (s *applies) Read(ctx context.Context, applyID string) (*Apply, error) {
// Logs retrieves the logs of an apply.
func (s *applies) Logs(ctx context.Context, applyID string) (io.Reader, error) {
if !validStringID(&applyID) {
return nil, errors.New("Invalid value for apply ID")
return nil, errors.New("invalid value for apply ID")
}
// Get the apply to make sure it exists.
@ -101,12 +101,12 @@ func (s *applies) Logs(ctx context.Context, applyID string) (io.Reader, error) {
// Return an error if the log URL is empty.
if a.LogReadURL == "" {
return nil, fmt.Errorf("Apply %s does not have a log URL", applyID)
return nil, fmt.Errorf("apply %s does not have a log URL", applyID)
}
u, err := url.Parse(a.LogReadURL)
if err != nil {
return nil, fmt.Errorf("Invalid log URL: %v", err)
return nil, fmt.Errorf("invalid log URL: %v", err)
}
done := func() (bool, error) {

View File

@ -101,7 +101,7 @@ type ConfigurationVersionListOptions struct {
// List returns all configuration versions of a workspace.
func (s *configurationVersions) List(ctx context.Context, workspaceID string, options ConfigurationVersionListOptions) (*ConfigurationVersionList, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("Invalid value for workspace ID")
return nil, errors.New("invalid value for workspace ID")
}
u := fmt.Sprintf("workspaces/%s/configuration-versions", url.QueryEscape(workspaceID))
@ -137,7 +137,7 @@ type ConfigurationVersionCreateOptions struct {
// configuration version will be usable once data is uploaded to it.
func (s *configurationVersions) Create(ctx context.Context, workspaceID string, options ConfigurationVersionCreateOptions) (*ConfigurationVersion, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("Invalid value for workspace ID")
return nil, errors.New("invalid value for workspace ID")
}
// Make sure we don't send a user provided ID.
@ -161,7 +161,7 @@ func (s *configurationVersions) Create(ctx context.Context, workspaceID string,
// Read a configuration version by its ID.
func (s *configurationVersions) Read(ctx context.Context, cvID string) (*ConfigurationVersion, error) {
if !validStringID(&cvID) {
return nil, errors.New("Invalid value for configuration version ID")
return nil, errors.New("invalid value for configuration version ID")
}
u := fmt.Sprintf("configuration-versions/%s", url.QueryEscape(cvID))

14
vendor/github.com/hashicorp/go-tfe/go.mod generated vendored Normal file
View File

@ -0,0 +1,14 @@
module github.com/hashicorp/go-tfe
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-querystring v1.0.0
github.com/hashicorp/go-cleanhttp v0.5.0
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6
github.com/hashicorp/go-slug v0.1.0
github.com/hashicorp/go-uuid v1.0.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c
)

20
vendor/github.com/hashicorp/go-tfe/go.sum generated vendored Normal file
View File

@ -0,0 +1,20 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6 h1:qCv4319q2q7XKn0MQbi8p37hsJ+9Xo8e6yojA73JVxk=
github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6/go.mod h1:fXcdFsQoipQa7mwORhKad5jmDCeSy/RCGzWA08PO0lM=
github.com/hashicorp/go-slug v0.1.0 h1:MJGEiOwRGrQCBmMMZABHqIESySFJ4ajrsjgDI4/aFI0=
github.com/hashicorp/go-slug v0.1.0/go.mod h1:+zDycQOzGqOqMW7Kn2fp9vz/NtqpMLQlgb9JUF+0km4=
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d h1:Z4EH+5EffvBEhh37F0C0DnpklTMh00JOkjW5zK3ofBI=
github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d/go.mod h1:BSTlc8jOjh0niykqEGVXOLXdi9o0r0kR8tCYiMvjFgw=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -63,8 +63,13 @@ func (r *LogReader) read(l []byte) (int, error) {
}
req = req.WithContext(r.ctx)
// Attach the default headers.
for k, v := range r.client.headers {
req.Header[k] = v
}
// Retrieve the next chunk.
resp, err := r.client.http.Do(req)
resp, err := r.client.http.HTTPClient.Do(req)
if err != nil {
return 0, err
}

View File

@ -83,7 +83,7 @@ type OAuthClientListOptions struct {
// List all the OAuth clients for a given organization.
func (s *oAuthClients) List(ctx context.Context, organization string, options OAuthClientListOptions) (*OAuthClientList, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/oauth-clients", url.QueryEscape(organization))
@ -121,16 +121,16 @@ type OAuthClientCreateOptions struct {
func (o OAuthClientCreateOptions) valid() error {
if !validString(o.APIURL) {
return errors.New("APIURL is required")
return errors.New("API URL is required")
}
if !validString(o.HTTPURL) {
return errors.New("HTTPURL is required")
return errors.New("HTTP URL is required")
}
if !validString(o.OAuthToken) {
return errors.New("OAuthToken is required")
return errors.New("OAuth token is required")
}
if o.ServiceProvider == nil {
return errors.New("ServiceProvider is required")
return errors.New("service provider is required")
}
return nil
}
@ -138,7 +138,7 @@ func (o OAuthClientCreateOptions) valid() error {
// Create an OAuth client to connect an organization and a VCS provider.
func (s *oAuthClients) Create(ctx context.Context, organization string, options OAuthClientCreateOptions) (*OAuthClient, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
if err := options.valid(); err != nil {
return nil, err
@ -165,7 +165,7 @@ func (s *oAuthClients) Create(ctx context.Context, organization string, options
// Read an OAuth client by its ID.
func (s *oAuthClients) Read(ctx context.Context, oAuthClientID string) (*OAuthClient, error) {
if !validStringID(&oAuthClientID) {
return nil, errors.New("Invalid value for OAuth client ID")
return nil, errors.New("invalid value for OAuth client ID")
}
u := fmt.Sprintf("oauth-clients/%s", url.QueryEscape(oAuthClientID))
@ -186,7 +186,7 @@ func (s *oAuthClients) Read(ctx context.Context, oAuthClientID string) (*OAuthCl
// Delete an OAuth client by its ID.
func (s *oAuthClients) Delete(ctx context.Context, oAuthClientID string) error {
if !validStringID(&oAuthClientID) {
return errors.New("Invalid value for OAuth client ID")
return errors.New("invalid value for OAuth client ID")
}
u := fmt.Sprintf("oauth-clients/%s", url.QueryEscape(oAuthClientID))

View File

@ -62,7 +62,7 @@ type OAuthTokenListOptions struct {
// List all the OAuth tokens for a given organization.
func (s *oAuthTokens) List(ctx context.Context, organization string, options OAuthTokenListOptions) (*OAuthTokenList, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/oauth-tokens", url.QueryEscape(organization))
@ -83,7 +83,7 @@ func (s *oAuthTokens) List(ctx context.Context, organization string, options OAu
// Read an OAuth token by its ID.
func (s *oAuthTokens) Read(ctx context.Context, oAuthTokenID string) (*OAuthToken, error) {
if !validStringID(&oAuthTokenID) {
return nil, errors.New("Invalid value for OAuth token ID")
return nil, errors.New("invalid value for OAuth token ID")
}
u := fmt.Sprintf("oauth-tokens/%s", url.QueryEscape(oAuthTokenID))
@ -113,7 +113,7 @@ type OAuthTokenUpdateOptions struct {
// Update an existing OAuth token.
func (s *oAuthTokens) Update(ctx context.Context, oAuthTokenID string, options OAuthTokenUpdateOptions) (*OAuthToken, error) {
if !validStringID(&oAuthTokenID) {
return nil, errors.New("Invalid value for OAuth token ID")
return nil, errors.New("invalid value for OAuth token ID")
}
// Make sure we don't send a user provided ID.
@ -137,7 +137,7 @@ func (s *oAuthTokens) Update(ctx context.Context, oAuthTokenID string, options O
// Delete an OAuth token by its ID.
func (s *oAuthTokens) Delete(ctx context.Context, oAuthTokenID string) error {
if !validStringID(&oAuthTokenID) {
return errors.New("Invalid value for OAuth token ID")
return errors.New("invalid value for OAuth token ID")
}
u := fmt.Sprintf("oauth-tokens/%s", url.QueryEscape(oAuthTokenID))

View File

@ -147,13 +147,13 @@ type OrganizationCreateOptions struct {
func (o OrganizationCreateOptions) valid() error {
if !validString(o.Name) {
return errors.New("Name is required")
return errors.New("name is required")
}
if !validStringID(o.Name) {
return errors.New("Invalid value for name")
return errors.New("invalid value for name")
}
if !validString(o.Email) {
return errors.New("Email is required")
return errors.New("email is required")
}
return nil
}
@ -184,7 +184,7 @@ func (s *organizations) Create(ctx context.Context, options OrganizationCreateOp
// Read an organization by its name.
func (s *organizations) Read(ctx context.Context, organization string) (*Organization, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s", url.QueryEscape(organization))
@ -226,7 +226,7 @@ type OrganizationUpdateOptions struct {
// Update attributes of an existing organization.
func (s *organizations) Update(ctx context.Context, organization string, options OrganizationUpdateOptions) (*Organization, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
// Make sure we don't send a user provided ID.
@ -250,7 +250,7 @@ func (s *organizations) Update(ctx context.Context, organization string, options
// Delete an organization by its name.
func (s *organizations) Delete(ctx context.Context, organization string) error {
if !validStringID(&organization) {
return errors.New("Invalid value for organization")
return errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s", url.QueryEscape(organization))
@ -265,7 +265,7 @@ func (s *organizations) Delete(ctx context.Context, organization string) error {
// Capacity shows the currently used capacity of an organization.
func (s *organizations) Capacity(ctx context.Context, organization string) (*Capacity, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/capacity", url.QueryEscape(organization))
@ -291,7 +291,7 @@ type RunQueueOptions struct {
// RunQueue shows the current run queue of an organization.
func (s *organizations) RunQueue(ctx context.Context, organization string, options RunQueueOptions) (*RunQueue, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/runs/queue", url.QueryEscape(organization))

View File

@ -44,7 +44,7 @@ type OrganizationToken struct {
// Generate a new organization token, replacing any existing token.
func (s *organizationTokens) Generate(ctx context.Context, organization string) (*OrganizationToken, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/authentication-token", url.QueryEscape(organization))
@ -65,7 +65,7 @@ func (s *organizationTokens) Generate(ctx context.Context, organization string)
// Read an organization token.
func (s *organizationTokens) Read(ctx context.Context, organization string) (*OrganizationToken, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/authentication-token", url.QueryEscape(organization))
@ -86,7 +86,7 @@ func (s *organizationTokens) Read(ctx context.Context, organization string) (*Or
// Delete an organization token.
func (s *organizationTokens) Delete(ctx context.Context, organization string) error {
if !validStringID(&organization) {
return errors.New("Invalid value for organization")
return errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/authentication-token", url.QueryEscape(organization))

View File

@ -70,7 +70,7 @@ type PlanStatusTimestamps struct {
// Read a plan by its ID.
func (s *plans) Read(ctx context.Context, planID string) (*Plan, error) {
if !validStringID(&planID) {
return nil, errors.New("Invalid value for plan ID")
return nil, errors.New("invalid value for plan ID")
}
u := fmt.Sprintf("plans/%s", url.QueryEscape(planID))
@ -91,7 +91,7 @@ func (s *plans) Read(ctx context.Context, planID string) (*Plan, error) {
// Logs retrieves the logs of a plan.
func (s *plans) Logs(ctx context.Context, planID string) (io.Reader, error) {
if !validStringID(&planID) {
return nil, errors.New("Invalid value for plan ID")
return nil, errors.New("invalid value for plan ID")
}
// Get the plan to make sure it exists.
@ -102,12 +102,12 @@ func (s *plans) Logs(ctx context.Context, planID string) (io.Reader, error) {
// Return an error if the log URL is empty.
if p.LogReadURL == "" {
return nil, fmt.Errorf("Plan %s does not have a log URL", planID)
return nil, fmt.Errorf("plan %s does not have a log URL", planID)
}
u, err := url.Parse(p.LogReadURL)
if err != nil {
return nil, fmt.Errorf("Invalid log URL: %v", err)
return nil, fmt.Errorf("invalid log URL: %v", err)
}
done := func() (bool, error) {

View File

@ -62,10 +62,15 @@ type PolicyList struct {
// Policy represents a Terraform Enterprise policy.
type Policy struct {
ID string `jsonapi:"primary,policies"`
Name string `jsonapi:"attr,name"`
Enforce []*Enforcement `jsonapi:"attr,enforce"`
UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"`
ID string `jsonapi:"primary,policies"`
Name string `jsonapi:"attr,name"`
Description string `jsonapi:"attr,description"`
Enforce []*Enforcement `jsonapi:"attr,enforce"`
PolicySetCount int `jsonapi:"attr,policy-set-count"`
UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"`
// Relations
Organization *Organization `jsonapi:"relation,organization"`
}
// Enforcement describes a enforcement.
@ -77,12 +82,15 @@ type Enforcement struct {
// PolicyListOptions represents the options for listing policies.
type PolicyListOptions struct {
ListOptions
// A search string (partial policy name) used to filter the results.
Search *string `url:"search[name],omitempty"`
}
// List all the policies for a given organization
func (s *policies) List(ctx context.Context, organization string, options PolicyListOptions) (*PolicyList, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/policies", url.QueryEscape(organization))
@ -108,6 +116,9 @@ type PolicyCreateOptions struct {
// The name of the policy.
Name *string `jsonapi:"attr,name"`
// A description of the policy's purpose.
Description *string `jsonapi:"attr,description,omitempty"`
// The enforcements of the policy.
Enforce []*EnforcementOptions `jsonapi:"attr,enforce"`
}
@ -120,20 +131,20 @@ type EnforcementOptions struct {
func (o PolicyCreateOptions) valid() error {
if !validString(o.Name) {
return errors.New("Name is required")
return errors.New("name is required")
}
if !validStringID(o.Name) {
return errors.New("Invalid value for name")
return errors.New("invalid value for name")
}
if o.Enforce == nil {
return errors.New("Enforce is required")
return errors.New("enforce is required")
}
for _, e := range o.Enforce {
if !validString(e.Path) {
return errors.New("Enforcement path is required")
return errors.New("enforcement path is required")
}
if e.Mode == nil {
return errors.New("Enforcement mode is required")
return errors.New("enforcement mode is required")
}
}
return nil
@ -142,7 +153,7 @@ func (o PolicyCreateOptions) valid() error {
// Create a policy and associate it with an organization.
func (s *policies) Create(ctx context.Context, organization string, options PolicyCreateOptions) (*Policy, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
if err := options.valid(); err != nil {
return nil, err
@ -169,7 +180,7 @@ func (s *policies) Create(ctx context.Context, organization string, options Poli
// Read a policy by its ID.
func (s *policies) Read(ctx context.Context, policyID string) (*Policy, error) {
if !validStringID(&policyID) {
return nil, errors.New("Invalid value for policy ID")
return nil, errors.New("invalid value for policy ID")
}
u := fmt.Sprintf("policies/%s", url.QueryEscape(policyID))
@ -192,24 +203,17 @@ type PolicyUpdateOptions struct {
// For internal use only!
ID string `jsonapi:"primary,policies"`
// The enforcements of the policy.
Enforce []*EnforcementOptions `jsonapi:"attr,enforce"`
}
// A description of the policy's purpose.
Description *string `jsonapi:"attr,description,omitempty"`
func (o PolicyUpdateOptions) valid() error {
if o.Enforce == nil {
return errors.New("Enforce is required")
}
return nil
// The enforcements of the policy.
Enforce []*EnforcementOptions `jsonapi:"attr,enforce,omitempty"`
}
// Update an existing policy.
func (s *policies) Update(ctx context.Context, policyID string, options PolicyUpdateOptions) (*Policy, error) {
if !validStringID(&policyID) {
return nil, errors.New("Invalid value for policy ID")
}
if err := options.valid(); err != nil {
return nil, err
return nil, errors.New("invalid value for policy ID")
}
// Make sure we don't send a user provided ID.
@ -233,7 +237,7 @@ func (s *policies) Update(ctx context.Context, policyID string, options PolicyUp
// Delete a policy by its ID.
func (s *policies) Delete(ctx context.Context, policyID string) error {
if !validStringID(&policyID) {
return errors.New("Invalid value for policy ID")
return errors.New("invalid value for policy ID")
}
u := fmt.Sprintf("policies/%s", url.QueryEscape(policyID))
@ -248,7 +252,7 @@ func (s *policies) Delete(ctx context.Context, policyID string) error {
// Upload the policy content of the policy.
func (s *policies) Upload(ctx context.Context, policyID string, content []byte) error {
if !validStringID(&policyID) {
return errors.New("Invalid value for policy ID")
return errors.New("invalid value for policy ID")
}
u := fmt.Sprintf("policies/%s/upload", url.QueryEscape(policyID))
@ -263,7 +267,7 @@ func (s *policies) Upload(ctx context.Context, policyID string, content []byte)
// Download the policy content of the policy.
func (s *policies) Download(ctx context.Context, policyID string) ([]byte, error) {
if !validStringID(&policyID) {
return nil, errors.New("Invalid value for policy ID")
return nil, errors.New("invalid value for policy ID")
}
u := fmt.Sprintf("policies/%s/download", url.QueryEscape(policyID))

View File

@ -118,7 +118,7 @@ type PolicyCheckListOptions struct {
// List all policy checks of the given run.
func (s *policyChecks) List(ctx context.Context, runID string, options PolicyCheckListOptions) (*PolicyCheckList, error) {
if !validStringID(&runID) {
return nil, errors.New("Invalid value for run ID")
return nil, errors.New("invalid value for run ID")
}
u := fmt.Sprintf("runs/%s/policy-checks", url.QueryEscape(runID))
@ -139,7 +139,7 @@ func (s *policyChecks) List(ctx context.Context, runID string, options PolicyChe
// Read a policy check by its ID.
func (s *policyChecks) Read(ctx context.Context, policyCheckID string) (*PolicyCheck, error) {
if !validStringID(&policyCheckID) {
return nil, errors.New("Invalid value for policy check ID")
return nil, errors.New("invalid value for policy check ID")
}
u := fmt.Sprintf("policy-checks/%s", url.QueryEscape(policyCheckID))
@ -160,7 +160,7 @@ func (s *policyChecks) Read(ctx context.Context, policyCheckID string) (*PolicyC
// Override a soft-mandatory or warning policy.
func (s *policyChecks) Override(ctx context.Context, policyCheckID string) (*PolicyCheck, error) {
if !validStringID(&policyCheckID) {
return nil, errors.New("Invalid value for policy check ID")
return nil, errors.New("invalid value for policy check ID")
}
u := fmt.Sprintf("policy-checks/%s/actions/override", url.QueryEscape(policyCheckID))
@ -181,7 +181,7 @@ func (s *policyChecks) Override(ctx context.Context, policyCheckID string) (*Pol
// Logs retrieves the logs of a policy check.
func (s *policyChecks) Logs(ctx context.Context, policyCheckID string) (io.Reader, error) {
if !validStringID(&policyCheckID) {
return nil, errors.New("Invalid value for policy check ID")
return nil, errors.New("invalid value for policy check ID")
}
// Loop until the context is canceled or the policy check is finished

381
vendor/github.com/hashicorp/go-tfe/policy_set.go generated vendored Normal file
View File

@ -0,0 +1,381 @@
package tfe
import (
"context"
"errors"
"fmt"
"net/url"
"time"
)
// Compile-time proof of interface implementation.
var _ PolicySets = (*policySets)(nil)
// PolicySets describes all the policy set related methods that the Terraform
// Enterprise API supports.
//
// TFE API docs: https://www.terraform.io/docs/enterprise/api/policies.html
type PolicySets interface {
// List all the policy sets for a given organization
List(ctx context.Context, organization string, options PolicySetListOptions) (*PolicySetList, error)
// Create a policy set and associate it with an organization.
Create(ctx context.Context, organization string, options PolicySetCreateOptions) (*PolicySet, error)
// Read a policy set by its ID.
Read(ctx context.Context, policySetID string) (*PolicySet, error)
// Update an existing policy set.
Update(ctx context.Context, policySetID string, options PolicySetUpdateOptions) (*PolicySet, error)
// Add policies to a policy set.
AddPolicies(ctx context.Context, policySetID string, options PolicySetAddPoliciesOptions) error
// Remove policies from a policy set.
RemovePolicies(ctx context.Context, policySetID string, options PolicySetRemovePoliciesOptions) error
// Attach a policy set to workspaces.
AttachToWorkspaces(ctx context.Context, policySetID string, options PolicySetAttachToWorkspacesOptions) error
// Detach a policy set from workspaces.
DetachFromWorkspaces(ctx context.Context, policySetID string, options PolicySetDetachFromWorkspacesOptions) error
// Delete a policy set by its ID.
Delete(ctx context.Context, policyID string) error
}
// policySets implements PolicySets.
type policySets struct {
client *Client
}
// PolicySetList represents a list of policy sets..
type PolicySetList struct {
*Pagination
Items []*PolicySet
}
// PolicySet represents a Terraform Enterprise policy set.
type PolicySet struct {
ID string `jsonapi:"primary,policy-sets"`
Name string `jsonapi:"attr,name"`
Description string `jsonapi:"attr,description"`
Global bool `jsonapi:"attr,global"`
PolicyCount int `jsonapi:"attr,policy-count"`
WorkspaceCount int `jsonapi:"attr,workspace-count"`
CreatedAt time.Time `jsonapi:"attr,created-at,iso8601"`
UpdatedAt time.Time `jsonapi:"attr,updated-at,iso8601"`
// Relations
Organization *Organization `jsonapi:"relation,organization"`
Policies []*Policy `jsonapi:"relation,policies"`
Workspaces []*Workspace `jsonapi:"relation,workspaces"`
}
// PolicySetListOptions represents the options for listing policy sets.
type PolicySetListOptions struct {
ListOptions
// A search string (partial policy set name) used to filter the results.
Search *string `url:"search[name],omitempty"`
}
// List all the policies for a given organization
func (s *policySets) List(ctx context.Context, organization string, options PolicySetListOptions) (*PolicySetList, error) {
if !validStringID(&organization) {
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/policy-sets", url.QueryEscape(organization))
req, err := s.client.newRequest("GET", u, &options)
if err != nil {
return nil, err
}
psl := &PolicySetList{}
err = s.client.do(ctx, req, psl)
if err != nil {
return nil, err
}
return psl, nil
}
// PolicySetCreateOptions represents the options for creating a new policy set.
type PolicySetCreateOptions struct {
// For internal use only!
ID string `jsonapi:"primary,policy-sets"`
// The name of the policy set.
Name *string `jsonapi:"attr,name"`
// The description of the policy set.
Description *string `jsonapi:"attr,description,omitempty"`
// Whether or not the policy set is global.
Global *bool `jsonapi:"attr,global,omitempty"`
// The initial members of the policy set.
Policies []*Policy `jsonapi:"relation,policies,omitempty"`
// The initial list of workspaces the policy set should be attached to.
Workspaces []*Workspace `jsonapi:"relation,workspaces,omitempty"`
}
func (o PolicySetCreateOptions) valid() error {
if !validString(o.Name) {
return errors.New("name is required")
}
if !validStringID(o.Name) {
return errors.New("invalid value for name")
}
return nil
}
// Create a policy set and associate it with an organization.
func (s *policySets) Create(ctx context.Context, organization string, options PolicySetCreateOptions) (*PolicySet, error) {
if !validStringID(&organization) {
return nil, errors.New("invalid value for organization")
}
if err := options.valid(); err != nil {
return nil, err
}
// Make sure we don't send a user provided ID.
options.ID = ""
u := fmt.Sprintf("organizations/%s/policy-sets", url.QueryEscape(organization))
req, err := s.client.newRequest("POST", u, &options)
if err != nil {
return nil, err
}
ps := &PolicySet{}
err = s.client.do(ctx, req, ps)
if err != nil {
return nil, err
}
return ps, err
}
// Read a policy set by its ID.
func (s *policySets) Read(ctx context.Context, policySetID string) (*PolicySet, error) {
if !validStringID(&policySetID) {
return nil, errors.New("invalid value for policy set ID")
}
u := fmt.Sprintf("policy-sets/%s", url.QueryEscape(policySetID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}
ps := &PolicySet{}
err = s.client.do(ctx, req, ps)
if err != nil {
return nil, err
}
return ps, err
}
// PolicySetUpdateOptions represents the options for updating a policy set.
type PolicySetUpdateOptions struct {
// For internal use only!
ID string `jsonapi:"primary,policy-sets"`
/// The name of the policy set.
Name *string `jsonapi:"attr,name,omitempty"`
// The description of the policy set.
Description *string `jsonapi:"attr,description,omitempty"`
// Whether or not the policy set is global.
Global *bool `jsonapi:"attr,global,omitempty"`
}
func (o PolicySetUpdateOptions) valid() error {
if o.Name != nil && !validStringID(o.Name) {
return errors.New("invalid value for name")
}
return nil
}
// Update an existing policy set.
func (s *policySets) Update(ctx context.Context, policySetID string, options PolicySetUpdateOptions) (*PolicySet, error) {
if !validStringID(&policySetID) {
return nil, errors.New("invalid value for policy set ID")
}
if err := options.valid(); err != nil {
return nil, err
}
// Make sure we don't send a user provided ID.
options.ID = ""
u := fmt.Sprintf("policy-sets/%s", url.QueryEscape(policySetID))
req, err := s.client.newRequest("PATCH", u, &options)
if err != nil {
return nil, err
}
ps := &PolicySet{}
err = s.client.do(ctx, req, ps)
if err != nil {
return nil, err
}
return ps, err
}
// PolicySetAddPoliciesOptions represents the options for adding policies to a policy set.
type PolicySetAddPoliciesOptions struct {
/// The policies to add to the policy set.
Policies []*Policy
}
func (o PolicySetAddPoliciesOptions) valid() error {
if o.Policies == nil {
return errors.New("policies is required")
}
if len(o.Policies) == 0 {
return errors.New("must provide at least one policy")
}
return nil
}
// Add policies to a policy set
func (s *policySets) AddPolicies(ctx context.Context, policySetID string, options PolicySetAddPoliciesOptions) error {
if !validStringID(&policySetID) {
return errors.New("invalid value for policy set ID")
}
if err := options.valid(); err != nil {
return err
}
u := fmt.Sprintf("policy-sets/%s/relationships/policies", url.QueryEscape(policySetID))
req, err := s.client.newRequest("POST", u, options.Policies)
if err != nil {
return err
}
return s.client.do(ctx, req, nil)
}
// PolicySetRemovePoliciesOptions represents the options for removing policies from a policy set.
type PolicySetRemovePoliciesOptions struct {
/// The policies to remove from the policy set.
Policies []*Policy
}
func (o PolicySetRemovePoliciesOptions) valid() error {
if o.Policies == nil {
return errors.New("policies is required")
}
if len(o.Policies) == 0 {
return errors.New("must provide at least one policy")
}
return nil
}
// Remove policies from a policy set
func (s *policySets) RemovePolicies(ctx context.Context, policySetID string, options PolicySetRemovePoliciesOptions) error {
if !validStringID(&policySetID) {
return errors.New("invalid value for policy set ID")
}
if err := options.valid(); err != nil {
return err
}
u := fmt.Sprintf("policy-sets/%s/relationships/policies", url.QueryEscape(policySetID))
req, err := s.client.newRequest("DELETE", u, options.Policies)
if err != nil {
return err
}
return s.client.do(ctx, req, nil)
}
// PolicySetAttachToWorkspacesOptions represents the options for attaching a policy set to workspaces.
type PolicySetAttachToWorkspacesOptions struct {
/// The workspaces on which to attach the policy set.
Workspaces []*Workspace
}
func (o PolicySetAttachToWorkspacesOptions) valid() error {
if o.Workspaces == nil {
return errors.New("workspaces is required")
}
if len(o.Workspaces) == 0 {
return errors.New("must provide at least one workspace")
}
return nil
}
// Attach a policy set to workspaces
func (s *policySets) AttachToWorkspaces(ctx context.Context, policySetID string, options PolicySetAttachToWorkspacesOptions) error {
if !validStringID(&policySetID) {
return errors.New("invalid value for policy set ID")
}
if err := options.valid(); err != nil {
return err
}
u := fmt.Sprintf("policy-sets/%s/relationships/workspaces", url.QueryEscape(policySetID))
req, err := s.client.newRequest("POST", u, options.Workspaces)
if err != nil {
return err
}
return s.client.do(ctx, req, nil)
}
// PolicySetDetachFromWorkspacesOptions represents the options for detaching a policy set from workspaces.
type PolicySetDetachFromWorkspacesOptions struct {
/// The workspaces from which to detach the policy set.
Workspaces []*Workspace
}
func (o PolicySetDetachFromWorkspacesOptions) valid() error {
if o.Workspaces == nil {
return errors.New("workspaces is required")
}
if len(o.Workspaces) == 0 {
return errors.New("must provide at least one workspace")
}
return nil
}
// Detach a policy set from workspaces
func (s *policySets) DetachFromWorkspaces(ctx context.Context, policySetID string, options PolicySetDetachFromWorkspacesOptions) error {
if !validStringID(&policySetID) {
return errors.New("invalid value for policy set ID")
}
if err := options.valid(); err != nil {
return err
}
u := fmt.Sprintf("policy-sets/%s/relationships/workspaces", url.QueryEscape(policySetID))
req, err := s.client.newRequest("DELETE", u, options.Workspaces)
if err != nil {
return err
}
return s.client.do(ctx, req, nil)
}
// Delete a policy set by its ID.
func (s *policySets) Delete(ctx context.Context, policySetID string) error {
if !validStringID(&policySetID) {
return errors.New("invalid value for policy set ID")
}
u := fmt.Sprintf("policy-sets/%s", url.QueryEscape(policySetID))
req, err := s.client.newRequest("DELETE", u, nil)
if err != nil {
return err
}
return s.client.do(ctx, req, nil)
}

View File

@ -136,7 +136,7 @@ type RunListOptions struct {
// List all the runs of the given workspace.
func (s *runs) List(ctx context.Context, workspaceID string, options RunListOptions) (*RunList, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("Invalid value for workspace ID")
return nil, errors.New("invalid value for workspace ID")
}
u := fmt.Sprintf("workspaces/%s/runs", url.QueryEscape(workspaceID))
@ -177,7 +177,7 @@ type RunCreateOptions struct {
func (o RunCreateOptions) valid() error {
if o.Workspace == nil {
return errors.New("Workspace is required")
return errors.New("workspace is required")
}
return nil
}
@ -208,7 +208,7 @@ func (s *runs) Create(ctx context.Context, options RunCreateOptions) (*Run, erro
// Read a run by its ID.
func (s *runs) Read(ctx context.Context, runID string) (*Run, error) {
if !validStringID(&runID) {
return nil, errors.New("Invalid value for run ID")
return nil, errors.New("invalid value for run ID")
}
u := fmt.Sprintf("runs/%s", url.QueryEscape(runID))
@ -235,7 +235,7 @@ type RunApplyOptions struct {
// Apply a run by its ID.
func (s *runs) Apply(ctx context.Context, runID string, options RunApplyOptions) error {
if !validStringID(&runID) {
return errors.New("Invalid value for run ID")
return errors.New("invalid value for run ID")
}
u := fmt.Sprintf("runs/%s/actions/apply", url.QueryEscape(runID))
@ -256,7 +256,7 @@ type RunCancelOptions struct {
// Cancel a run by its ID.
func (s *runs) Cancel(ctx context.Context, runID string, options RunCancelOptions) error {
if !validStringID(&runID) {
return errors.New("Invalid value for run ID")
return errors.New("invalid value for run ID")
}
u := fmt.Sprintf("runs/%s/actions/cancel", url.QueryEscape(runID))
@ -277,7 +277,7 @@ type RunForceCancelOptions struct {
// ForceCancel is used to forcefully cancel a run by its ID.
func (s *runs) ForceCancel(ctx context.Context, runID string, options RunForceCancelOptions) error {
if !validStringID(&runID) {
return errors.New("Invalid value for run ID")
return errors.New("invalid value for run ID")
}
u := fmt.Sprintf("runs/%s/actions/force-cancel", url.QueryEscape(runID))
@ -298,7 +298,7 @@ type RunDiscardOptions struct {
// Discard a run by its ID.
func (s *runs) Discard(ctx context.Context, runID string, options RunDiscardOptions) error {
if !validStringID(&runID) {
return errors.New("Invalid value for run ID")
return errors.New("invalid value for run ID")
}
u := fmt.Sprintf("runs/%s/actions/discard", url.QueryEscape(runID))

View File

@ -57,7 +57,7 @@ type SSHKeyListOptions struct {
// List all the SSH keys for a given organization
func (s *sshKeys) List(ctx context.Context, organization string, options SSHKeyListOptions) (*SSHKeyList, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/ssh-keys", url.QueryEscape(organization))
@ -89,10 +89,10 @@ type SSHKeyCreateOptions struct {
func (o SSHKeyCreateOptions) valid() error {
if !validString(o.Name) {
return errors.New("Name is required")
return errors.New("name is required")
}
if !validString(o.Value) {
return errors.New("Value is required")
return errors.New("value is required")
}
return nil
}
@ -100,7 +100,7 @@ func (o SSHKeyCreateOptions) valid() error {
// Create an SSH key and associate it with an organization.
func (s *sshKeys) Create(ctx context.Context, organization string, options SSHKeyCreateOptions) (*SSHKey, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
if err := options.valid(); err != nil {
@ -128,7 +128,7 @@ func (s *sshKeys) Create(ctx context.Context, organization string, options SSHKe
// Read an SSH key by its ID.
func (s *sshKeys) Read(ctx context.Context, sshKeyID string) (*SSHKey, error) {
if !validStringID(&sshKeyID) {
return nil, errors.New("Invalid value for SSH key ID")
return nil, errors.New("invalid value for SSH key ID")
}
u := fmt.Sprintf("ssh-keys/%s", url.QueryEscape(sshKeyID))
@ -161,7 +161,7 @@ type SSHKeyUpdateOptions struct {
// Update an SSH key by its ID.
func (s *sshKeys) Update(ctx context.Context, sshKeyID string, options SSHKeyUpdateOptions) (*SSHKey, error) {
if !validStringID(&sshKeyID) {
return nil, errors.New("Invalid value for SSH key ID")
return nil, errors.New("invalid value for SSH key ID")
}
// Make sure we don't send a user provided ID.
@ -185,7 +185,7 @@ func (s *sshKeys) Update(ctx context.Context, sshKeyID string, options SSHKeyUpd
// Delete an SSH key by its ID.
func (s *sshKeys) Delete(ctx context.Context, sshKeyID string) error {
if !validStringID(&sshKeyID) {
return errors.New("Invalid value for SSH key ID")
return errors.New("invalid value for SSH key ID")
}
u := fmt.Sprintf("ssh-keys/%s", url.QueryEscape(sshKeyID))

View File

@ -67,10 +67,10 @@ type StateVersionListOptions struct {
func (o StateVersionListOptions) valid() error {
if !validString(o.Organization) {
return errors.New("Organization is required")
return errors.New("organization is required")
}
if !validString(o.Workspace) {
return errors.New("Workspace is required")
return errors.New("workspace is required")
}
return nil
}
@ -121,10 +121,10 @@ func (o StateVersionCreateOptions) valid() error {
return errors.New("MD5 is required")
}
if o.Serial == nil {
return errors.New("Serial is required")
return errors.New("serial is required")
}
if !validString(o.State) {
return errors.New("State is required")
return errors.New("state is required")
}
return nil
}
@ -132,7 +132,7 @@ func (o StateVersionCreateOptions) valid() error {
// Create a new state version for the given workspace.
func (s *stateVersions) Create(ctx context.Context, workspaceID string, options StateVersionCreateOptions) (*StateVersion, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("Invalid value for workspace ID")
return nil, errors.New("invalid value for workspace ID")
}
if err := options.valid(); err != nil {
return nil, err
@ -159,7 +159,7 @@ func (s *stateVersions) Create(ctx context.Context, workspaceID string, options
// Read a state version by its ID.
func (s *stateVersions) Read(ctx context.Context, svID string) (*StateVersion, error) {
if !validStringID(&svID) {
return nil, errors.New("Invalid value for state version ID")
return nil, errors.New("invalid value for state version ID")
}
u := fmt.Sprintf("state-versions/%s", url.QueryEscape(svID))
@ -180,7 +180,7 @@ func (s *stateVersions) Read(ctx context.Context, svID string) (*StateVersion, e
// Current reads the latest available state from the given workspace.
func (s *stateVersions) Current(ctx context.Context, workspaceID string) (*StateVersion, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("Invalid value for workspace ID")
return nil, errors.New("invalid value for workspace ID")
}
u := fmt.Sprintf("workspaces/%s/current-state-version", url.QueryEscape(workspaceID))

View File

@ -64,7 +64,7 @@ type TeamListOptions struct {
// List all the teams of the given organization.
func (s *teams) List(ctx context.Context, organization string, options TeamListOptions) (*TeamList, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/teams", url.QueryEscape(organization))
@ -93,10 +93,10 @@ type TeamCreateOptions struct {
func (o TeamCreateOptions) valid() error {
if !validString(o.Name) {
return errors.New("Name is required")
return errors.New("name is required")
}
if !validStringID(o.Name) {
return errors.New("Invalid value for name")
return errors.New("invalid value for name")
}
return nil
}
@ -104,7 +104,7 @@ func (o TeamCreateOptions) valid() error {
// Create a new team with the given options.
func (s *teams) Create(ctx context.Context, organization string, options TeamCreateOptions) (*Team, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
if err := options.valid(); err != nil {
return nil, err
@ -131,7 +131,7 @@ func (s *teams) Create(ctx context.Context, organization string, options TeamCre
// Read a single team by its ID.
func (s *teams) Read(ctx context.Context, teamID string) (*Team, error) {
if !validStringID(&teamID) {
return nil, errors.New("Invalid value for team ID")
return nil, errors.New("invalid value for team ID")
}
u := fmt.Sprintf("teams/%s", url.QueryEscape(teamID))
@ -152,7 +152,7 @@ func (s *teams) Read(ctx context.Context, teamID string) (*Team, error) {
// Delete a team by its ID.
func (s *teams) Delete(ctx context.Context, teamID string) error {
if !validStringID(&teamID) {
return errors.New("Invalid value for team ID")
return errors.New("invalid value for team ID")
}
u := fmt.Sprintf("teams/%s", url.QueryEscape(teamID))

View File

@ -68,10 +68,10 @@ type TeamAccessListOptions struct {
func (o TeamAccessListOptions) valid() error {
if !validString(o.WorkspaceID) {
return errors.New("Workspace ID is required")
return errors.New("workspace ID is required")
}
if !validStringID(o.WorkspaceID) {
return errors.New("Invalid value for workspace ID")
return errors.New("invalid value for workspace ID")
}
return nil
}
@ -113,13 +113,13 @@ type TeamAccessAddOptions struct {
func (o TeamAccessAddOptions) valid() error {
if o.Access == nil {
return errors.New("Access is required")
return errors.New("access is required")
}
if o.Team == nil {
return errors.New("Team is required")
return errors.New("team is required")
}
if o.Workspace == nil {
return errors.New("Workspace is required")
return errors.New("workspace is required")
}
return nil
}
@ -150,7 +150,7 @@ func (s *teamAccesses) Add(ctx context.Context, options TeamAccessAddOptions) (*
// Read a team access by its ID.
func (s *teamAccesses) Read(ctx context.Context, teamAccessID string) (*TeamAccess, error) {
if !validStringID(&teamAccessID) {
return nil, errors.New("Invalid value for team access ID")
return nil, errors.New("invalid value for team access ID")
}
u := fmt.Sprintf("team-workspaces/%s", url.QueryEscape(teamAccessID))
@ -171,7 +171,7 @@ func (s *teamAccesses) Read(ctx context.Context, teamAccessID string) (*TeamAcce
// Remove team access from a workspace.
func (s *teamAccesses) Remove(ctx context.Context, teamAccessID string) error {
if !validStringID(&teamAccessID) {
return errors.New("Invalid value for team access ID")
return errors.New("invalid value for team access ID")
}
u := fmt.Sprintf("team-workspaces/%s", url.QueryEscape(teamAccessID))

View File

@ -38,7 +38,7 @@ type teamMember struct {
// List all members of a team.
func (s *teamMembers) List(ctx context.Context, teamID string) ([]*User, error) {
if !validStringID(&teamID) {
return nil, errors.New("Invalid value for team ID")
return nil, errors.New("invalid value for team ID")
}
options := struct {
@ -69,10 +69,10 @@ type TeamMemberAddOptions struct {
func (o *TeamMemberAddOptions) valid() error {
if o.Usernames == nil {
return errors.New("Usernames is required")
return errors.New("usernames is required")
}
if len(o.Usernames) == 0 {
return errors.New("Invalid value for usernames")
return errors.New("invalid value for usernames")
}
return nil
}
@ -80,7 +80,7 @@ func (o *TeamMemberAddOptions) valid() error {
// Add multiple users to a team.
func (s *teamMembers) Add(ctx context.Context, teamID string, options TeamMemberAddOptions) error {
if !validStringID(&teamID) {
return errors.New("Invalid value for team ID")
return errors.New("invalid value for team ID")
}
if err := options.valid(); err != nil {
return err
@ -107,10 +107,10 @@ type TeamMemberRemoveOptions struct {
func (o *TeamMemberRemoveOptions) valid() error {
if o.Usernames == nil {
return errors.New("Usernames is required")
return errors.New("usernames is required")
}
if len(o.Usernames) == 0 {
return errors.New("Invalid value for usernames")
return errors.New("invalid value for usernames")
}
return nil
}
@ -118,7 +118,7 @@ func (o *TeamMemberRemoveOptions) valid() error {
// Remove multiple users from a team.
func (s *teamMembers) Remove(ctx context.Context, teamID string, options TeamMemberRemoveOptions) error {
if !validStringID(&teamID) {
return errors.New("Invalid value for team ID")
return errors.New("invalid value for team ID")
}
if err := options.valid(); err != nil {
return err

View File

@ -44,7 +44,7 @@ type TeamToken struct {
// Generate a new team token, replacing any existing token.
func (s *teamTokens) Generate(ctx context.Context, teamID string) (*TeamToken, error) {
if !validStringID(&teamID) {
return nil, errors.New("Invalid value for team ID")
return nil, errors.New("invalid value for team ID")
}
u := fmt.Sprintf("teams/%s/authentication-token", url.QueryEscape(teamID))
@ -65,7 +65,7 @@ func (s *teamTokens) Generate(ctx context.Context, teamID string) (*TeamToken, e
// Read a team token by its ID.
func (s *teamTokens) Read(ctx context.Context, teamID string) (*TeamToken, error) {
if !validStringID(&teamID) {
return nil, errors.New("Invalid value for team ID")
return nil, errors.New("invalid value for team ID")
}
u := fmt.Sprintf("teams/%s/authentication-token", url.QueryEscape(teamID))
@ -86,7 +86,7 @@ func (s *teamTokens) Read(ctx context.Context, teamID string) (*TeamToken, error
// Delete a team token by its ID.
func (s *teamTokens) Delete(ctx context.Context, teamID string) error {
if !validStringID(&teamID) {
return errors.New("Invalid value for team ID")
return errors.New("invalid value for team ID")
}
u := fmt.Sprintf("teams/%s/authentication-token", url.QueryEscape(teamID))

View File

@ -7,30 +7,37 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"os"
"reflect"
"strconv"
"strings"
"time"
"github.com/google/go-querystring/query"
"github.com/hashicorp/go-cleanhttp"
retryablehttp "github.com/hashicorp/go-retryablehttp"
"github.com/svanharmelen/jsonapi"
"golang.org/x/time/rate"
)
const (
userAgent = "go-tfe"
headerRateLimit = "X-RateLimit-Limit"
headerRateReset = "X-RateLimit-Reset"
// DefaultAddress of Terraform Enterprise.
DefaultAddress = "https://app.terraform.io"
// DefaultBasePath on which the API is served.
DefaultBasePath = "/api/v2/"
)
const (
userAgent = "go-tfe"
)
var (
// random is used to generate pseudo-random numbers.
random = rand.New(rand.NewSource(time.Now().UnixNano()))
// ErrUnauthorized is returned when a receiving a 401.
ErrUnauthorized = errors.New("unauthorized")
// ErrResourceNotFound is returned when a receiving a 404.
@ -82,7 +89,8 @@ type Client struct {
baseURL *url.URL
token string
headers http.Header
http *http.Client
http *retryablehttp.Client
limiter *rate.Limiter
Applies Applies
ConfigurationVersions ConfigurationVersions
@ -93,6 +101,7 @@ type Client struct {
Plans Plans
Policies Policies
PolicyChecks PolicyChecks
PolicySets PolicySets
Runs Runs
SSHKeys SSHKeys
StateVersions StateVersions
@ -131,7 +140,7 @@ func NewClient(cfg *Config) (*Client, error) {
// Parse the address to make sure its a valid URL.
baseURL, err := url.Parse(config.Address)
if err != nil {
return nil, fmt.Errorf("Invalid address: %v", err)
return nil, fmt.Errorf("invalid address: %v", err)
}
baseURL.Path = config.BasePath
@ -141,7 +150,7 @@ func NewClient(cfg *Config) (*Client, error) {
// This value must be provided by the user.
if config.Token == "" {
return nil, fmt.Errorf("Missing API token")
return nil, fmt.Errorf("missing API token")
}
// Create the client.
@ -149,7 +158,20 @@ func NewClient(cfg *Config) (*Client, error) {
baseURL: baseURL,
token: config.Token,
headers: config.Headers,
http: config.HTTPClient,
http: &retryablehttp.Client{
Backoff: rateLimitBackoff,
CheckRetry: rateLimitRetry,
ErrorHandler: retryablehttp.PassthroughErrorHandler,
HTTPClient: config.HTTPClient,
RetryWaitMin: 100 * time.Millisecond,
RetryWaitMax: 300 * time.Millisecond,
RetryMax: 5,
},
}
// Configure the rate limiter.
if err := client.configureLimiter(); err != nil {
return nil, err
}
// Create the services.
@ -162,6 +184,7 @@ func NewClient(cfg *Config) (*Client, error) {
client.Plans = &plans{client: client}
client.Policies = &policies{client: client}
client.PolicyChecks = &policyChecks{client: client}
client.PolicySets = &policySets{client: client}
client.Runs = &runs{client: client}
client.SSHKeys = &sshKeys{client: client}
client.StateVersions = &stateVersions{client: client}
@ -176,6 +199,96 @@ func NewClient(cfg *Config) (*Client, error) {
return client, nil
}
// rateLimitRetry provides a callback for Client.CheckRetry, which will only
// retry when receiving a 429 response which indicates being rate limited.
func rateLimitRetry(ctx context.Context, resp *http.Response, err error) (bool, error) {
// Do not retry on context.Canceled or context.DeadlineExceeded.
if ctx.Err() != nil {
return false, ctx.Err()
}
// Do not retry on any unexpected errors.
if err != nil {
return false, err
}
// Only retry when we are rate limited.
if resp.StatusCode == 429 {
return true, nil
}
return false, nil
}
// rateLimitBackoff provides a callback for Client.Backoff which will use the
// X-RateLimit_Reset header to determine the time to wait. We add some jitter
// to prevent a thundering herd.
//
// min and max are mainly used for bounding the jitter that will be added to
// the reset time retrieved from the headers. But if the final wait time is
// less then min, min will be used instead.
func rateLimitBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
// First create some jitter bounded by the min and max durations.
jitter := time.Duration(rand.Float64() * float64(max-min))
if resp != nil {
if v := resp.Header.Get(headerRateReset); v != "" {
if reset, _ := strconv.ParseFloat(v, 64); reset > 0 {
// Only update min if the given time to wait is longer.
if wait := time.Duration(reset * 1e9); wait > min {
min = wait
}
}
}
}
return min + jitter
}
// configureLimiter configures the rate limiter.
func (c *Client) configureLimiter() error {
u, err := c.baseURL.Parse("/")
if err != nil {
return err
}
// Create a new request.
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
return err
}
// Attach the default headers.
for k, v := range c.headers {
req.Header[k] = v
}
// Make a single request to retrieve the rate limit headers.
resp, err := c.http.HTTPClient.Do(req)
if err != nil {
return err
}
resp.Body.Close()
// Set default values for when rate limiting is disabled.
limit := rate.Inf
burst := 0
if v := resp.Header.Get(headerRateLimit); v != "" {
if rateLimit, _ := strconv.ParseFloat(v, 64); rateLimit > 0 {
// Configure the limit and burst using a split of 2/3 for the limit and
// 1/3 for the burst. This enables clients to burst 1/3 of the allowed
// calls before the limiter kicks in. The remaining calls will then be
// spread out evenly using intervals of time.Second / limit which should
// prevent hitting the rate limit.
limit = rate.Limit(rateLimit * 0.66)
burst = int(rateLimit * 0.33)
}
}
// Create a new limiter using the calculated values.
c.limiter = rate.NewLimiter(limit, burst)
return nil
}
// ListOptions is used to specify pagination options when making API requests.
// Pagination allows breaking up large result sets into chunks, or "pages".
type ListOptions struct {
@ -202,30 +315,20 @@ type Pagination struct {
// If v is supplied, the value will be JSONAPI encoded and included as the
// request body. If the method is GET, the value will be parsed and added as
// query parameters.
func (c *Client) newRequest(method, path string, v interface{}) (*http.Request, error) {
func (c *Client) newRequest(method, path string, v interface{}) (*retryablehttp.Request, error) {
u, err := c.baseURL.Parse(path)
if err != nil {
return nil, err
}
req := &http.Request{
Method: method,
URL: u,
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: make(http.Header),
Host: u.Host,
}
// Set default headers.
for k, v := range c.headers {
req.Header[k] = v
}
// Create a request specific headers map.
reqHeaders := make(http.Header)
reqHeaders.Set("Authorization", "Bearer "+c.token)
var body interface{}
switch method {
case "GET":
req.Header.Set("Accept", "application/vnd.api+json")
reqHeaders.Set("Accept", "application/vnd.api+json")
if v != nil {
q, err := query.Values(v)
@ -235,37 +338,36 @@ func (c *Client) newRequest(method, path string, v interface{}) (*http.Request,
u.RawQuery = q.Encode()
}
case "DELETE", "PATCH", "POST":
req.Header.Set("Accept", "application/vnd.api+json")
req.Header.Set("Content-Type", "application/vnd.api+json")
reqHeaders.Set("Accept", "application/vnd.api+json")
reqHeaders.Set("Content-Type", "application/vnd.api+json")
if v != nil {
var body bytes.Buffer
if err := jsonapi.MarshalPayloadWithoutIncluded(&body, v); err != nil {
buf := bytes.NewBuffer(nil)
if err := jsonapi.MarshalPayloadWithoutIncluded(buf, v); err != nil {
return nil, err
}
req.Body = ioutil.NopCloser(&body)
req.ContentLength = int64(body.Len())
body = buf
}
case "PUT":
req.Header.Set("Accept", "application/json")
req.Header.Set("Content-Type", "application/octet-stream")
if v != nil {
switch v := v.(type) {
case *bytes.Buffer:
req.Body = ioutil.NopCloser(v)
req.ContentLength = int64(v.Len())
case []byte:
req.Body = ioutil.NopCloser(bytes.NewReader(v))
req.ContentLength = int64(len(v))
default:
return nil, fmt.Errorf("Unexpected type: %T", v)
}
}
reqHeaders.Set("Accept", "application/json")
reqHeaders.Set("Content-Type", "application/octet-stream")
body = v
}
// Set the authorization header.
req.Header.Set("Authorization", "Bearer "+c.token)
req, err := retryablehttp.NewRequest(method, u.String(), body)
if err != nil {
return nil, err
}
// Set the default headers.
for k, v := range c.headers {
req.Header[k] = v
}
// Set the request specific headers.
for k, v := range reqHeaders {
req.Header[k] = v
}
return req, nil
}
@ -279,7 +381,13 @@ func (c *Client) newRequest(method, path string, v interface{}) (*http.Request,
//
// The provided ctx must be non-nil. If it is canceled or times out, ctx.Err()
// will be returned.
func (c *Client) do(ctx context.Context, req *http.Request, v interface{}) error {
func (c *Client) do(ctx context.Context, req *retryablehttp.Request, v interface{}) error {
// Wait will block until the limiter can obtain a new token
// or returns an error if the given context is canceled.
if err := c.limiter.Wait(ctx); err != nil {
return err
}
// Add the context to the request.
req = req.WithContext(ctx)

View File

@ -73,10 +73,10 @@ type VariableListOptions struct {
func (o VariableListOptions) valid() error {
if !validString(o.Organization) {
return errors.New("Organization is required")
return errors.New("organization is required")
}
if !validString(o.Workspace) {
return errors.New("Workspace is required")
return errors.New("workspace is required")
}
return nil
}
@ -127,16 +127,16 @@ type VariableCreateOptions struct {
func (o VariableCreateOptions) valid() error {
if !validString(o.Key) {
return errors.New("Key is required")
return errors.New("key is required")
}
if !validString(o.Value) {
return errors.New("Value is required")
return errors.New("value is required")
}
if o.Category == nil {
return errors.New("Category is required")
return errors.New("category is required")
}
if o.Workspace == nil {
return errors.New("Workspace is required")
return errors.New("workspace is required")
}
return nil
}
@ -167,7 +167,7 @@ func (s *variables) Create(ctx context.Context, options VariableCreateOptions) (
// Read a variable by its ID.
func (s *variables) Read(ctx context.Context, variableID string) (*Variable, error) {
if !validStringID(&variableID) {
return nil, errors.New("Invalid value for variable ID")
return nil, errors.New("invalid value for variable ID")
}
u := fmt.Sprintf("vars/%s", url.QueryEscape(variableID))
@ -206,7 +206,7 @@ type VariableUpdateOptions struct {
// Update values of an existing variable.
func (s *variables) Update(ctx context.Context, variableID string, options VariableUpdateOptions) (*Variable, error) {
if !validStringID(&variableID) {
return nil, errors.New("Invalid value for variable ID")
return nil, errors.New("invalid value for variable ID")
}
// Make sure we don't send a user provided ID.
@ -230,7 +230,7 @@ func (s *variables) Update(ctx context.Context, variableID string, options Varia
// Delete a variable by its ID.
func (s *variables) Delete(ctx context.Context, variableID string) error {
if !validStringID(&variableID) {
return errors.New("Invalid value for variable ID")
return errors.New("invalid value for variable ID")
}
u := fmt.Sprintf("vars/%s", url.QueryEscape(variableID))

View File

@ -112,7 +112,7 @@ type WorkspaceListOptions struct {
// List all the workspaces within an organization.
func (s *workspaces) List(ctx context.Context, organization string, options WorkspaceListOptions) (*WorkspaceList, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
u := fmt.Sprintf("organizations/%s/workspaces", url.QueryEscape(organization))
@ -173,10 +173,10 @@ type VCSRepoOptions struct {
func (o WorkspaceCreateOptions) valid() error {
if !validString(o.Name) {
return errors.New("Name is required")
return errors.New("name is required")
}
if !validStringID(o.Name) {
return errors.New("Invalid value for name")
return errors.New("invalid value for name")
}
return nil
}
@ -184,7 +184,7 @@ func (o WorkspaceCreateOptions) valid() error {
// Create is used to create a new workspace.
func (s *workspaces) Create(ctx context.Context, organization string, options WorkspaceCreateOptions) (*Workspace, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
if err := options.valid(); err != nil {
return nil, err
@ -211,10 +211,10 @@ func (s *workspaces) Create(ctx context.Context, organization string, options Wo
// Read a workspace by its name.
func (s *workspaces) Read(ctx context.Context, organization, workspace string) (*Workspace, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
if !validStringID(&workspace) {
return nil, errors.New("Invalid value for workspace")
return nil, errors.New("invalid value for workspace")
}
u := fmt.Sprintf(
@ -270,10 +270,10 @@ type WorkspaceUpdateOptions struct {
// Update settings of an existing workspace.
func (s *workspaces) Update(ctx context.Context, organization, workspace string, options WorkspaceUpdateOptions) (*Workspace, error) {
if !validStringID(&organization) {
return nil, errors.New("Invalid value for organization")
return nil, errors.New("invalid value for organization")
}
if !validStringID(&workspace) {
return nil, errors.New("Invalid value for workspace")
return nil, errors.New("invalid value for workspace")
}
// Make sure we don't send a user provided ID.
@ -301,10 +301,10 @@ func (s *workspaces) Update(ctx context.Context, organization, workspace string,
// Delete a workspace by its name.
func (s *workspaces) Delete(ctx context.Context, organization, workspace string) error {
if !validStringID(&organization) {
return errors.New("Invalid value for organization")
return errors.New("invalid value for organization")
}
if !validStringID(&workspace) {
return errors.New("Invalid value for workspace")
return errors.New("invalid value for workspace")
}
u := fmt.Sprintf(
@ -329,7 +329,7 @@ type WorkspaceLockOptions struct {
// Lock a workspace by its ID.
func (s *workspaces) Lock(ctx context.Context, workspaceID string, options WorkspaceLockOptions) (*Workspace, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("Invalid value for workspace ID")
return nil, errors.New("invalid value for workspace ID")
}
u := fmt.Sprintf("workspaces/%s/actions/lock", url.QueryEscape(workspaceID))
@ -350,7 +350,7 @@ func (s *workspaces) Lock(ctx context.Context, workspaceID string, options Works
// Unlock a workspace by its ID.
func (s *workspaces) Unlock(ctx context.Context, workspaceID string) (*Workspace, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("Invalid value for workspace ID")
return nil, errors.New("invalid value for workspace ID")
}
u := fmt.Sprintf("workspaces/%s/actions/unlock", url.QueryEscape(workspaceID))
@ -383,7 +383,7 @@ func (o WorkspaceAssignSSHKeyOptions) valid() error {
return errors.New("SSH key ID is required")
}
if !validStringID(o.SSHKeyID) {
return errors.New("Invalid value for SSH key ID")
return errors.New("invalid value for SSH key ID")
}
return nil
}
@ -391,7 +391,7 @@ func (o WorkspaceAssignSSHKeyOptions) valid() error {
// AssignSSHKey to a workspace.
func (s *workspaces) AssignSSHKey(ctx context.Context, workspaceID string, options WorkspaceAssignSSHKeyOptions) (*Workspace, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("Invalid value for workspace ID")
return nil, errors.New("invalid value for workspace ID")
}
if err := options.valid(); err != nil {
return nil, err
@ -428,7 +428,7 @@ type workspaceUnassignSSHKeyOptions struct {
// UnassignSSHKey from a workspace.
func (s *workspaces) UnassignSSHKey(ctx context.Context, workspaceID string) (*Workspace, error) {
if !validStringID(&workspaceID) {
return nil, errors.New("Invalid value for workspace ID")
return nil, errors.New("invalid value for workspace ID")
}
u := fmt.Sprintf("workspaces/%s/relationships/ssh-key", url.QueryEscape(workspaceID))

3
vendor/golang.org/x/time/AUTHORS generated vendored Normal file
View File

@ -0,0 +1,3 @@
# This source code refers to The Go Authors for copyright purposes.
# The master list of authors is in the main Go distribution,
# visible at http://tip.golang.org/AUTHORS.

3
vendor/golang.org/x/time/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,3 @@
# This source code was written by the Go contributors.
# The master list of contributors is in the main Go distribution,
# visible at http://tip.golang.org/CONTRIBUTORS.

27
vendor/golang.org/x/time/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/time/PATENTS generated vendored Normal file
View File

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

374
vendor/golang.org/x/time/rate/rate.go generated vendored Normal file
View File

@ -0,0 +1,374 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package rate provides a rate limiter.
package rate
import (
"context"
"fmt"
"math"
"sync"
"time"
)
// Limit defines the maximum frequency of some events.
// Limit is represented as number of events per second.
// A zero Limit allows no events.
type Limit float64
// Inf is the infinite rate limit; it allows all events (even if burst is zero).
const Inf = Limit(math.MaxFloat64)
// Every converts a minimum time interval between events to a Limit.
func Every(interval time.Duration) Limit {
if interval <= 0 {
return Inf
}
return 1 / Limit(interval.Seconds())
}
// A Limiter controls how frequently events are allowed to happen.
// It implements a "token bucket" of size b, initially full and refilled
// at rate r tokens per second.
// Informally, in any large enough time interval, the Limiter limits the
// rate to r tokens per second, with a maximum burst size of b events.
// As a special case, if r == Inf (the infinite rate), b is ignored.
// See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets.
//
// The zero value is a valid Limiter, but it will reject all events.
// Use NewLimiter to create non-zero Limiters.
//
// Limiter has three main methods, Allow, Reserve, and Wait.
// Most callers should use Wait.
//
// Each of the three methods consumes a single token.
// They differ in their behavior when no token is available.
// If no token is available, Allow returns false.
// If no token is available, Reserve returns a reservation for a future token
// and the amount of time the caller must wait before using it.
// If no token is available, Wait blocks until one can be obtained
// or its associated context.Context is canceled.
//
// The methods AllowN, ReserveN, and WaitN consume n tokens.
type Limiter struct {
limit Limit
burst int
mu sync.Mutex
tokens float64
// last is the last time the limiter's tokens field was updated
last time.Time
// lastEvent is the latest time of a rate-limited event (past or future)
lastEvent time.Time
}
// Limit returns the maximum overall event rate.
func (lim *Limiter) Limit() Limit {
lim.mu.Lock()
defer lim.mu.Unlock()
return lim.limit
}
// Burst returns the maximum burst size. Burst is the maximum number of tokens
// that can be consumed in a single call to Allow, Reserve, or Wait, so higher
// Burst values allow more events to happen at once.
// A zero Burst allows no events, unless limit == Inf.
func (lim *Limiter) Burst() int {
return lim.burst
}
// NewLimiter returns a new Limiter that allows events up to rate r and permits
// bursts of at most b tokens.
func NewLimiter(r Limit, b int) *Limiter {
return &Limiter{
limit: r,
burst: b,
}
}
// Allow is shorthand for AllowN(time.Now(), 1).
func (lim *Limiter) Allow() bool {
return lim.AllowN(time.Now(), 1)
}
// AllowN reports whether n events may happen at time now.
// Use this method if you intend to drop / skip events that exceed the rate limit.
// Otherwise use Reserve or Wait.
func (lim *Limiter) AllowN(now time.Time, n int) bool {
return lim.reserveN(now, n, 0).ok
}
// A Reservation holds information about events that are permitted by a Limiter to happen after a delay.
// A Reservation may be canceled, which may enable the Limiter to permit additional events.
type Reservation struct {
ok bool
lim *Limiter
tokens int
timeToAct time.Time
// This is the Limit at reservation time, it can change later.
limit Limit
}
// OK returns whether the limiter can provide the requested number of tokens
// within the maximum wait time. If OK is false, Delay returns InfDuration, and
// Cancel does nothing.
func (r *Reservation) OK() bool {
return r.ok
}
// Delay is shorthand for DelayFrom(time.Now()).
func (r *Reservation) Delay() time.Duration {
return r.DelayFrom(time.Now())
}
// InfDuration is the duration returned by Delay when a Reservation is not OK.
const InfDuration = time.Duration(1<<63 - 1)
// DelayFrom returns the duration for which the reservation holder must wait
// before taking the reserved action. Zero duration means act immediately.
// InfDuration means the limiter cannot grant the tokens requested in this
// Reservation within the maximum wait time.
func (r *Reservation) DelayFrom(now time.Time) time.Duration {
if !r.ok {
return InfDuration
}
delay := r.timeToAct.Sub(now)
if delay < 0 {
return 0
}
return delay
}
// Cancel is shorthand for CancelAt(time.Now()).
func (r *Reservation) Cancel() {
r.CancelAt(time.Now())
return
}
// CancelAt indicates that the reservation holder will not perform the reserved action
// and reverses the effects of this Reservation on the rate limit as much as possible,
// considering that other reservations may have already been made.
func (r *Reservation) CancelAt(now time.Time) {
if !r.ok {
return
}
r.lim.mu.Lock()
defer r.lim.mu.Unlock()
if r.lim.limit == Inf || r.tokens == 0 || r.timeToAct.Before(now) {
return
}
// calculate tokens to restore
// The duration between lim.lastEvent and r.timeToAct tells us how many tokens were reserved
// after r was obtained. These tokens should not be restored.
restoreTokens := float64(r.tokens) - r.limit.tokensFromDuration(r.lim.lastEvent.Sub(r.timeToAct))
if restoreTokens <= 0 {
return
}
// advance time to now
now, _, tokens := r.lim.advance(now)
// calculate new number of tokens
tokens += restoreTokens
if burst := float64(r.lim.burst); tokens > burst {
tokens = burst
}
// update state
r.lim.last = now
r.lim.tokens = tokens
if r.timeToAct == r.lim.lastEvent {
prevEvent := r.timeToAct.Add(r.limit.durationFromTokens(float64(-r.tokens)))
if !prevEvent.Before(now) {
r.lim.lastEvent = prevEvent
}
}
return
}
// Reserve is shorthand for ReserveN(time.Now(), 1).
func (lim *Limiter) Reserve() *Reservation {
return lim.ReserveN(time.Now(), 1)
}
// ReserveN returns a Reservation that indicates how long the caller must wait before n events happen.
// The Limiter takes this Reservation into account when allowing future events.
// ReserveN returns false if n exceeds the Limiter's burst size.
// Usage example:
// r := lim.ReserveN(time.Now(), 1)
// if !r.OK() {
// // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
// return
// }
// time.Sleep(r.Delay())
// Act()
// Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events.
// If you need to respect a deadline or cancel the delay, use Wait instead.
// To drop or skip events exceeding rate limit, use Allow instead.
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation {
r := lim.reserveN(now, n, InfDuration)
return &r
}
// Wait is shorthand for WaitN(ctx, 1).
func (lim *Limiter) Wait(ctx context.Context) (err error) {
return lim.WaitN(ctx, 1)
}
// WaitN blocks until lim permits n events to happen.
// It returns an error if n exceeds the Limiter's burst size, the Context is
// canceled, or the expected wait time exceeds the Context's Deadline.
// The burst limit is ignored if the rate limit is Inf.
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error) {
if n > lim.burst && lim.limit != Inf {
return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, lim.burst)
}
// Check if ctx is already cancelled
select {
case <-ctx.Done():
return ctx.Err()
default:
}
// Determine wait limit
now := time.Now()
waitLimit := InfDuration
if deadline, ok := ctx.Deadline(); ok {
waitLimit = deadline.Sub(now)
}
// Reserve
r := lim.reserveN(now, n, waitLimit)
if !r.ok {
return fmt.Errorf("rate: Wait(n=%d) would exceed context deadline", n)
}
// Wait if necessary
delay := r.DelayFrom(now)
if delay == 0 {
return nil
}
t := time.NewTimer(delay)
defer t.Stop()
select {
case <-t.C:
// We can proceed.
return nil
case <-ctx.Done():
// Context was canceled before we could proceed. Cancel the
// reservation, which may permit other events to proceed sooner.
r.Cancel()
return ctx.Err()
}
}
// SetLimit is shorthand for SetLimitAt(time.Now(), newLimit).
func (lim *Limiter) SetLimit(newLimit Limit) {
lim.SetLimitAt(time.Now(), newLimit)
}
// SetLimitAt sets a new Limit for the limiter. The new Limit, and Burst, may be violated
// or underutilized by those which reserved (using Reserve or Wait) but did not yet act
// before SetLimitAt was called.
func (lim *Limiter) SetLimitAt(now time.Time, newLimit Limit) {
lim.mu.Lock()
defer lim.mu.Unlock()
now, _, tokens := lim.advance(now)
lim.last = now
lim.tokens = tokens
lim.limit = newLimit
}
// reserveN is a helper method for AllowN, ReserveN, and WaitN.
// maxFutureReserve specifies the maximum reservation wait duration allowed.
// reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN.
func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation {
lim.mu.Lock()
if lim.limit == Inf {
lim.mu.Unlock()
return Reservation{
ok: true,
lim: lim,
tokens: n,
timeToAct: now,
}
}
now, last, tokens := lim.advance(now)
// Calculate the remaining number of tokens resulting from the request.
tokens -= float64(n)
// Calculate the wait duration
var waitDuration time.Duration
if tokens < 0 {
waitDuration = lim.limit.durationFromTokens(-tokens)
}
// Decide result
ok := n <= lim.burst && waitDuration <= maxFutureReserve
// Prepare reservation
r := Reservation{
ok: ok,
lim: lim,
limit: lim.limit,
}
if ok {
r.tokens = n
r.timeToAct = now.Add(waitDuration)
}
// Update state
if ok {
lim.last = now
lim.tokens = tokens
lim.lastEvent = r.timeToAct
} else {
lim.last = last
}
lim.mu.Unlock()
return r
}
// advance calculates and returns an updated state for lim resulting from the passage of time.
// lim is not changed.
func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) {
last := lim.last
if now.Before(last) {
last = now
}
// Avoid making delta overflow below when last is very old.
maxElapsed := lim.limit.durationFromTokens(float64(lim.burst) - lim.tokens)
elapsed := now.Sub(last)
if elapsed > maxElapsed {
elapsed = maxElapsed
}
// Calculate the new number of tokens, due to time that passed.
delta := lim.limit.tokensFromDuration(elapsed)
tokens := lim.tokens + delta
if burst := float64(lim.burst); tokens > burst {
tokens = burst
}
return now, last, tokens
}
// durationFromTokens is a unit conversion function from the number of tokens to the duration
// of time it takes to accumulate them at a rate of limit tokens per second.
func (limit Limit) durationFromTokens(tokens float64) time.Duration {
seconds := tokens / float64(limit)
return time.Nanosecond * time.Duration(1e9*seconds)
}
// tokensFromDuration is a unit conversion function from a time duration to the number of tokens
// which could be accumulated during that duration at a rate of limit tokens per second.
func (limit Limit) tokensFromDuration(d time.Duration) float64 {
return d.Seconds() * float64(limit)
}

8
vendor/modules.txt vendored
View File

@ -296,7 +296,7 @@ github.com/hashicorp/consul/testutil/retry
github.com/hashicorp/errwrap
# github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
github.com/hashicorp/go-checkpoint
# github.com/hashicorp/go-cleanhttp v0.0.0-20171130225243-06c9ea3a335b
# github.com/hashicorp/go-cleanhttp v0.5.0
github.com/hashicorp/go-cleanhttp
# github.com/hashicorp/go-getter v0.0.0-20180327010114-90bb99a48d86
github.com/hashicorp/go-getter
@ -307,7 +307,7 @@ github.com/hashicorp/go-hclog
github.com/hashicorp/go-multierror
# github.com/hashicorp/go-plugin v0.0.0-20181002195811-1faddcf740b6
github.com/hashicorp/go-plugin
# github.com/hashicorp/go-retryablehttp v0.0.0-20160930035102-6e85be8fee1d
# github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6
github.com/hashicorp/go-retryablehttp
# github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90
github.com/hashicorp/go-rootcerts
@ -315,7 +315,7 @@ github.com/hashicorp/go-rootcerts
github.com/hashicorp/go-safetemp
# github.com/hashicorp/go-slug v0.1.0
github.com/hashicorp/go-slug
# github.com/hashicorp/go-tfe v0.2.6
# github.com/hashicorp/go-tfe v0.2.9
github.com/hashicorp/go-tfe
# github.com/hashicorp/go-uuid v1.0.0
github.com/hashicorp/go-uuid
@ -543,6 +543,8 @@ golang.org/x/text/encoding/unicode
golang.org/x/text/internal/tag
golang.org/x/text/internal/utf8internal
golang.org/x/text/runes
# golang.org/x/time v0.0.0-20181108054448-85acf8d2951c
golang.org/x/time/rate
# google.golang.org/api v0.0.0-20181015145326-625cd1887957
google.golang.org/api/iterator
google.golang.org/api/option