From d0c320f1485a2d28d32344965575308aad891e3f Mon Sep 17 00:00:00 2001 From: Sander van Harmelen Date: Tue, 13 Nov 2018 11:17:39 +0100 Subject: [PATCH] depencies: update `go-tfe` --- go.mod | 10 +- go.sum | 18 +- .../github.com/hashicorp/go-cleanhttp/go.mod | 1 + .../hashicorp/go-cleanhttp/handlers.go | 43 ++ .../hashicorp/go-retryablehttp/.travis.yml | 2 +- .../hashicorp/go-retryablehttp/README.md | 13 +- .../hashicorp/go-retryablehttp/client.go | 298 +++++++++++--- vendor/github.com/hashicorp/go-tfe/README.md | 1 + vendor/github.com/hashicorp/go-tfe/apply.go | 8 +- .../hashicorp/go-tfe/configuration_version.go | 6 +- vendor/github.com/hashicorp/go-tfe/go.mod | 14 + vendor/github.com/hashicorp/go-tfe/go.sum | 20 + .../github.com/hashicorp/go-tfe/logreader.go | 7 +- .../hashicorp/go-tfe/oauth_client.go | 16 +- .../hashicorp/go-tfe/oauth_token.go | 8 +- .../hashicorp/go-tfe/organization.go | 16 +- .../hashicorp/go-tfe/organization_token.go | 6 +- vendor/github.com/hashicorp/go-tfe/plan.go | 8 +- vendor/github.com/hashicorp/go-tfe/policy.go | 58 +-- .../hashicorp/go-tfe/policy_check.go | 8 +- .../github.com/hashicorp/go-tfe/policy_set.go | 381 ++++++++++++++++++ vendor/github.com/hashicorp/go-tfe/run.go | 14 +- vendor/github.com/hashicorp/go-tfe/ssh_key.go | 14 +- .../hashicorp/go-tfe/state_version.go | 14 +- vendor/github.com/hashicorp/go-tfe/team.go | 12 +- .../hashicorp/go-tfe/team_access.go | 14 +- .../hashicorp/go-tfe/team_member.go | 14 +- .../github.com/hashicorp/go-tfe/team_token.go | 6 +- vendor/github.com/hashicorp/go-tfe/tfe.go | 206 +++++++--- .../github.com/hashicorp/go-tfe/variable.go | 18 +- .../github.com/hashicorp/go-tfe/workspace.go | 30 +- vendor/golang.org/x/time/AUTHORS | 3 + vendor/golang.org/x/time/CONTRIBUTORS | 3 + vendor/golang.org/x/time/LICENSE | 27 ++ vendor/golang.org/x/time/PATENTS | 22 + vendor/golang.org/x/time/rate/rate.go | 374 +++++++++++++++++ vendor/modules.txt | 8 +- 37 files changed, 1464 insertions(+), 257 deletions(-) create mode 100644 vendor/github.com/hashicorp/go-cleanhttp/go.mod create mode 100644 vendor/github.com/hashicorp/go-cleanhttp/handlers.go create mode 100644 vendor/github.com/hashicorp/go-tfe/go.mod create mode 100644 vendor/github.com/hashicorp/go-tfe/go.sum create mode 100644 vendor/github.com/hashicorp/go-tfe/policy_set.go create mode 100644 vendor/golang.org/x/time/AUTHORS create mode 100644 vendor/golang.org/x/time/CONTRIBUTORS create mode 100644 vendor/golang.org/x/time/LICENSE create mode 100644 vendor/golang.org/x/time/PATENTS create mode 100644 vendor/golang.org/x/time/rate/rate.go diff --git a/go.mod b/go.mod index 3a7705311..cb722b2f5 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 576fed37a..e7924ef91 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/vendor/github.com/hashicorp/go-cleanhttp/go.mod b/vendor/github.com/hashicorp/go-cleanhttp/go.mod new file mode 100644 index 000000000..310f07569 --- /dev/null +++ b/vendor/github.com/hashicorp/go-cleanhttp/go.mod @@ -0,0 +1 @@ +module github.com/hashicorp/go-cleanhttp diff --git a/vendor/github.com/hashicorp/go-cleanhttp/handlers.go b/vendor/github.com/hashicorp/go-cleanhttp/handlers.go new file mode 100644 index 000000000..7eda3777f --- /dev/null +++ b/vendor/github.com/hashicorp/go-cleanhttp/handlers.go @@ -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 + }) +} diff --git a/vendor/github.com/hashicorp/go-retryablehttp/.travis.yml b/vendor/github.com/hashicorp/go-retryablehttp/.travis.yml index 54a6c7a22..2df4e7dfa 100644 --- a/vendor/github.com/hashicorp/go-retryablehttp/.travis.yml +++ b/vendor/github.com/hashicorp/go-retryablehttp/.travis.yml @@ -3,7 +3,7 @@ sudo: false language: go go: - - 1.6.3 + - 1.8.1 branches: only: diff --git a/vendor/github.com/hashicorp/go-retryablehttp/README.md b/vendor/github.com/hashicorp/go-retryablehttp/README.md index 0d6f9ed40..ccdc7e87c 100644 --- a/vendor/github.com/hashicorp/go-retryablehttp/README.md +++ b/vendor/github.com/hashicorp/go-retryablehttp/README.md @@ -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 =========== diff --git a/vendor/github.com/hashicorp/go-retryablehttp/client.go b/vendor/github.com/hashicorp/go-retryablehttp/client.go index 198779bdf..21f45e5ed 100644 --- a/vendor/github.com/hashicorp/go-retryablehttp/client.go +++ b/vendor/github.com/hashicorp/go-retryablehttp/client.go @@ -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 -} diff --git a/vendor/github.com/hashicorp/go-tfe/README.md b/vendor/github.com/hashicorp/go-tfe/README.md index 05bbc78d8..4ca44f4fe 100644 --- a/vendor/github.com/hashicorp/go-tfe/README.md +++ b/vendor/github.com/hashicorp/go-tfe/README.md @@ -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) diff --git a/vendor/github.com/hashicorp/go-tfe/apply.go b/vendor/github.com/hashicorp/go-tfe/apply.go index d99eaab53..2c92896bd 100644 --- a/vendor/github.com/hashicorp/go-tfe/apply.go +++ b/vendor/github.com/hashicorp/go-tfe/apply.go @@ -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) { diff --git a/vendor/github.com/hashicorp/go-tfe/configuration_version.go b/vendor/github.com/hashicorp/go-tfe/configuration_version.go index 168c1c6dd..64c0db0a0 100644 --- a/vendor/github.com/hashicorp/go-tfe/configuration_version.go +++ b/vendor/github.com/hashicorp/go-tfe/configuration_version.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/go.mod b/vendor/github.com/hashicorp/go-tfe/go.mod new file mode 100644 index 000000000..8cad701d8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/go.mod @@ -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 +) diff --git a/vendor/github.com/hashicorp/go-tfe/go.sum b/vendor/github.com/hashicorp/go-tfe/go.sum new file mode 100644 index 000000000..dcc948753 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/go.sum @@ -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= diff --git a/vendor/github.com/hashicorp/go-tfe/logreader.go b/vendor/github.com/hashicorp/go-tfe/logreader.go index cdc1aad9b..aee4472fe 100644 --- a/vendor/github.com/hashicorp/go-tfe/logreader.go +++ b/vendor/github.com/hashicorp/go-tfe/logreader.go @@ -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 } diff --git a/vendor/github.com/hashicorp/go-tfe/oauth_client.go b/vendor/github.com/hashicorp/go-tfe/oauth_client.go index be31fd8e3..b0b16bfb2 100644 --- a/vendor/github.com/hashicorp/go-tfe/oauth_client.go +++ b/vendor/github.com/hashicorp/go-tfe/oauth_client.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/oauth_token.go b/vendor/github.com/hashicorp/go-tfe/oauth_token.go index 2367a1e3b..73ab48010 100644 --- a/vendor/github.com/hashicorp/go-tfe/oauth_token.go +++ b/vendor/github.com/hashicorp/go-tfe/oauth_token.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/organization.go b/vendor/github.com/hashicorp/go-tfe/organization.go index f4759a231..32e289ed6 100644 --- a/vendor/github.com/hashicorp/go-tfe/organization.go +++ b/vendor/github.com/hashicorp/go-tfe/organization.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/organization_token.go b/vendor/github.com/hashicorp/go-tfe/organization_token.go index 33368da0b..e2b0e0dfa 100644 --- a/vendor/github.com/hashicorp/go-tfe/organization_token.go +++ b/vendor/github.com/hashicorp/go-tfe/organization_token.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/plan.go b/vendor/github.com/hashicorp/go-tfe/plan.go index 194ce65fb..31aef138d 100644 --- a/vendor/github.com/hashicorp/go-tfe/plan.go +++ b/vendor/github.com/hashicorp/go-tfe/plan.go @@ -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) { diff --git a/vendor/github.com/hashicorp/go-tfe/policy.go b/vendor/github.com/hashicorp/go-tfe/policy.go index 80926af37..b558b2608 100644 --- a/vendor/github.com/hashicorp/go-tfe/policy.go +++ b/vendor/github.com/hashicorp/go-tfe/policy.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/policy_check.go b/vendor/github.com/hashicorp/go-tfe/policy_check.go index d5417e300..7196568d1 100644 --- a/vendor/github.com/hashicorp/go-tfe/policy_check.go +++ b/vendor/github.com/hashicorp/go-tfe/policy_check.go @@ -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 diff --git a/vendor/github.com/hashicorp/go-tfe/policy_set.go b/vendor/github.com/hashicorp/go-tfe/policy_set.go new file mode 100644 index 000000000..15400da14 --- /dev/null +++ b/vendor/github.com/hashicorp/go-tfe/policy_set.go @@ -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) +} diff --git a/vendor/github.com/hashicorp/go-tfe/run.go b/vendor/github.com/hashicorp/go-tfe/run.go index 59501e6ad..b310bf600 100644 --- a/vendor/github.com/hashicorp/go-tfe/run.go +++ b/vendor/github.com/hashicorp/go-tfe/run.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/ssh_key.go b/vendor/github.com/hashicorp/go-tfe/ssh_key.go index b76a170db..8735d0717 100644 --- a/vendor/github.com/hashicorp/go-tfe/ssh_key.go +++ b/vendor/github.com/hashicorp/go-tfe/ssh_key.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/state_version.go b/vendor/github.com/hashicorp/go-tfe/state_version.go index 89bcfda1c..768bd5125 100644 --- a/vendor/github.com/hashicorp/go-tfe/state_version.go +++ b/vendor/github.com/hashicorp/go-tfe/state_version.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/team.go b/vendor/github.com/hashicorp/go-tfe/team.go index e6a69c3d2..0fa2fb61d 100644 --- a/vendor/github.com/hashicorp/go-tfe/team.go +++ b/vendor/github.com/hashicorp/go-tfe/team.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/team_access.go b/vendor/github.com/hashicorp/go-tfe/team_access.go index 33abc3225..8d2d7e46c 100644 --- a/vendor/github.com/hashicorp/go-tfe/team_access.go +++ b/vendor/github.com/hashicorp/go-tfe/team_access.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/team_member.go b/vendor/github.com/hashicorp/go-tfe/team_member.go index 297d58a6b..8ab97fdac 100644 --- a/vendor/github.com/hashicorp/go-tfe/team_member.go +++ b/vendor/github.com/hashicorp/go-tfe/team_member.go @@ -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 diff --git a/vendor/github.com/hashicorp/go-tfe/team_token.go b/vendor/github.com/hashicorp/go-tfe/team_token.go index baaf75789..6763b6604 100644 --- a/vendor/github.com/hashicorp/go-tfe/team_token.go +++ b/vendor/github.com/hashicorp/go-tfe/team_token.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/tfe.go b/vendor/github.com/hashicorp/go-tfe/tfe.go index 127d94a93..c38938564 100644 --- a/vendor/github.com/hashicorp/go-tfe/tfe.go +++ b/vendor/github.com/hashicorp/go-tfe/tfe.go @@ -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) diff --git a/vendor/github.com/hashicorp/go-tfe/variable.go b/vendor/github.com/hashicorp/go-tfe/variable.go index ba28404c9..9434cd413 100644 --- a/vendor/github.com/hashicorp/go-tfe/variable.go +++ b/vendor/github.com/hashicorp/go-tfe/variable.go @@ -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)) diff --git a/vendor/github.com/hashicorp/go-tfe/workspace.go b/vendor/github.com/hashicorp/go-tfe/workspace.go index 4d78a75d8..ca3b21d06 100644 --- a/vendor/github.com/hashicorp/go-tfe/workspace.go +++ b/vendor/github.com/hashicorp/go-tfe/workspace.go @@ -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)) diff --git a/vendor/golang.org/x/time/AUTHORS b/vendor/golang.org/x/time/AUTHORS new file mode 100644 index 000000000..15167cd74 --- /dev/null +++ b/vendor/golang.org/x/time/AUTHORS @@ -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. diff --git a/vendor/golang.org/x/time/CONTRIBUTORS b/vendor/golang.org/x/time/CONTRIBUTORS new file mode 100644 index 000000000..1c4577e96 --- /dev/null +++ b/vendor/golang.org/x/time/CONTRIBUTORS @@ -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. diff --git a/vendor/golang.org/x/time/LICENSE b/vendor/golang.org/x/time/LICENSE new file mode 100644 index 000000000..6a66aea5e --- /dev/null +++ b/vendor/golang.org/x/time/LICENSE @@ -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. diff --git a/vendor/golang.org/x/time/PATENTS b/vendor/golang.org/x/time/PATENTS new file mode 100644 index 000000000..733099041 --- /dev/null +++ b/vendor/golang.org/x/time/PATENTS @@ -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. diff --git a/vendor/golang.org/x/time/rate/rate.go b/vendor/golang.org/x/time/rate/rate.go new file mode 100644 index 000000000..ae93e2471 --- /dev/null +++ b/vendor/golang.org/x/time/rate/rate.go @@ -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) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 09369f8fe..ee1530091 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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