From 6a9dc20675c82a4201689f49b075a803ac50f41b Mon Sep 17 00:00:00 2001 From: janeczku Date: Tue, 31 May 2016 04:57:42 +0200 Subject: [PATCH] Better support for exposing certificate files in volumes * Store certificates in /etc/letsencrypt//certs// * Support for changing DNS names of existing certificate --- Dockerfile | 2 - Dockerfile.dev | 6 +- Godeps/Godeps.json | 120 +- .../aws-sdk-go/aws/client/default_retryer.go | 26 +- .../github.com/aws/aws-sdk-go/aws/config.go | 47 + .../aws-sdk-go/aws/corehandlers/handlers.go | 48 +- .../aws/corehandlers/param_validator.go | 148 +-- .../aws/credentials/static_provider.go | 2 +- .../aws-sdk-go/aws/request/http_request.go | 26 + .../aws/request/http_request_1_4.go | 24 + .../aws/aws-sdk-go/aws/request/request.go | 31 +- .../aws/aws-sdk-go/aws/request/validation.go | 234 ++++ .../aws/aws-sdk-go/aws/session/session.go | 2 +- .../github.com/aws/aws-sdk-go/aws/version.go | 2 +- .../aws/aws-sdk-go/private/signer/v4/v4.go | 47 +- .../aws/aws-sdk-go/service/route53/api.go | 1103 ++++++++++++++++- .../src/github.com/miekg/dns/README.md | 7 +- .../src/github.com/miekg/dns/client.go | 26 +- .../src/github.com/miekg/dns/defaults.go | 14 +- .../src/github.com/miekg/dns/edns.go | 39 +- .../src/github.com/miekg/dns/labels.go | 2 +- .../src/github.com/miekg/dns/msg.go | 14 +- .../github.com/miekg/dns/types_generate.go | 5 +- .../src/github.com/miekg/dns/udp.go | 2 +- .../src/github.com/miekg/dns/udp_other.go | 2 +- .../src/github.com/miekg/dns/udp_plan9.go | 34 + .../src/github.com/miekg/dns/xfr.go | 4 +- .../xenolf/lego/acme/dns_challenge.go | 8 +- .../src/github.com/xenolf/lego/acme/http.go | 5 +- .../providers/dns/cloudflare/cloudflare.go | 18 +- Makefile | 6 +- circle.yml | 15 +- context.go | 70 +- letsencrypt/account.go | 2 +- letsencrypt/client.go | 157 ++- letsencrypt/crypto.go | 2 +- main.go | 5 + manager.go | 60 +- 38 files changed, 1925 insertions(+), 440 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/http_request.go create mode 100644 Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/http_request_1_4.go create mode 100644 Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/validation.go create mode 100644 Godeps/_workspace/src/github.com/miekg/dns/udp_plan9.go diff --git a/Dockerfile b/Dockerfile index e83853f..54204d5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,4 @@ ADD https://github.com/janeczku/rancher-letsencrypt/releases/download/${LETSENCR RUN tar -zxvf /tmp/rancher-letsencrypt.tar.gz -C /usr/bin \ && chmod +x /usr/bin/rancher-letsencrypt -VOLUME /etc/letsencrypt - ENTRYPOINT ["/usr/bin/rancher-letsencrypt"] \ No newline at end of file diff --git a/Dockerfile.dev b/Dockerfile.dev index 913a0ae..b100114 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -7,8 +7,4 @@ ADD build/rancher-letsencrypt-linux-amd64 /usr/bin/rancher-letsencrypt RUN chmod +x /usr/bin/rancher-letsencrypt -VOLUME /etc/letsencrypt - -ENV DEBUG=true - -ENTRYPOINT ["/usr/bin/rancher-letsencrypt"] \ No newline at end of file +ENTRYPOINT ["/usr/bin/rancher-letsencrypt", "-debug"] \ No newline at end of file diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index d554f9d..9f5202c 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,7 +1,7 @@ { "ImportPath": "github.com/janeczku/rancher-letsencrypt", "GoVersion": "go1.5", - "GodepVersion": "v62", + "GodepVersion": "v70", "Deps": [ { "ImportPath": "github.com/Sirupsen/logrus", @@ -10,113 +10,113 @@ }, { "ImportPath": "github.com/aws/aws-sdk-go/aws", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/awserr", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/client", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/credentials", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/defaults", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/request", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/session", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/endpoints", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/restxml", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/waiter", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/route53", - "Comment": "v1.1.15", - "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" + "Comment": "v1.1.25", + "Rev": "02ee239c4ddbc6750fd0c3b5b5728b2b33aa92b4" }, { "ImportPath": "github.com/go-ini/ini", @@ -134,7 +134,7 @@ }, { "ImportPath": "github.com/miekg/dns", - "Rev": "b9171237b0642de1d8e8004f16869970e065f46b" + "Rev": "48ab6605c66ac797e07f615101c3e9e10e932b66" }, { "ImportPath": "github.com/rancher/go-rancher/client", @@ -147,38 +147,38 @@ }, { "ImportPath": "github.com/xenolf/lego/acme", - "Comment": "v0.3.1-5-g9484835", - "Rev": "948483535f53c34d144419869ecbed86251a30f6" + "Comment": "v0.3.1-15-g30a7a8e", + "Rev": "30a7a8e8821de3532192d1240a45e53c6204f603" }, { "ImportPath": "github.com/xenolf/lego/providers/dns/cloudflare", - "Comment": "v0.3.1-5-g9484835", - "Rev": "948483535f53c34d144419869ecbed86251a30f6" + "Comment": "v0.3.1-15-g30a7a8e", + "Rev": "30a7a8e8821de3532192d1240a45e53c6204f603" }, { "ImportPath": "github.com/xenolf/lego/providers/dns/digitalocean", - "Comment": "v0.3.1-5-g9484835", - "Rev": "948483535f53c34d144419869ecbed86251a30f6" + "Comment": "v0.3.1-15-g30a7a8e", + "Rev": "30a7a8e8821de3532192d1240a45e53c6204f603" }, { "ImportPath": "github.com/xenolf/lego/providers/dns/dnsimple", - "Comment": "v0.3.1-5-g9484835", - "Rev": "948483535f53c34d144419869ecbed86251a30f6" + "Comment": "v0.3.1-15-g30a7a8e", + "Rev": "30a7a8e8821de3532192d1240a45e53c6204f603" }, { "ImportPath": "github.com/xenolf/lego/providers/dns/dyn", - "Comment": "v0.3.1-5-g9484835", - "Rev": "948483535f53c34d144419869ecbed86251a30f6" + "Comment": "v0.3.1-15-g30a7a8e", + "Rev": "30a7a8e8821de3532192d1240a45e53c6204f603" }, { "ImportPath": "github.com/xenolf/lego/providers/dns/namecheap", - "Comment": "v0.3.1-5-g9484835", - "Rev": "948483535f53c34d144419869ecbed86251a30f6" + "Comment": "v0.3.1-15-g30a7a8e", + "Rev": "30a7a8e8821de3532192d1240a45e53c6204f603" }, { "ImportPath": "github.com/xenolf/lego/providers/dns/route53", - "Comment": "v0.3.1-5-g9484835", - "Rev": "948483535f53c34d144419869ecbed86251a30f6" + "Comment": "v0.3.1-15-g30a7a8e", + "Rev": "30a7a8e8821de3532192d1240a45e53c6204f603" }, { "ImportPath": "golang.org/x/crypto/ocsp", diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/client/default_retryer.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/client/default_retryer.go index f664caf..43a3676 100644 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/client/default_retryer.go +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/client/default_retryer.go @@ -2,6 +2,7 @@ package client import ( "math/rand" + "sync" "time" "github.com/aws/aws-sdk-go/aws/request" @@ -29,13 +30,15 @@ func (d DefaultRetryer) MaxRetries() int { return d.NumMaxRetries } +var seededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())}) + // RetryRules returns the delay duration before retrying this request again func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration { // Set the upper limit of delay in retrying at ~five minutes minTime := 30 throttle := d.shouldThrottle(r) if throttle { - minTime = 1000 + minTime = 500 } retryCount := r.RetryCount @@ -45,7 +48,7 @@ func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration { retryCount = 8 } - delay := (1 << uint(retryCount)) * (rand.Intn(30) + minTime) + delay := (1 << uint(retryCount)) * (seededRand.Intn(minTime) + minTime) return time.Duration(delay) * time.Millisecond } @@ -66,3 +69,22 @@ func (d DefaultRetryer) shouldThrottle(r *request.Request) bool { } return r.IsErrorThrottle() } + +// lockedSource is a thread-safe implementation of rand.Source +type lockedSource struct { + lk sync.Mutex + src rand.Source +} + +func (r *lockedSource) Int63() (n int64) { + r.lk.Lock() + n = r.src.Int63() + r.lk.Unlock() + return +} + +func (r *lockedSource) Seed(seed int64) { + r.lk.Lock() + r.src.Seed(seed) + r.lk.Unlock() +} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/config.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/config.go index 9e83e92..bfaa152 100644 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/config.go +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/config.go @@ -100,6 +100,31 @@ type Config struct { // Amazon S3: Virtual Hosting of Buckets S3ForcePathStyle *bool + // Set this to `true` to disable the SDK adding the `Expect: 100-Continue` + // header to PUT requests over 2MB of content. 100-Continue instructs the + // HTTP client not to send the body until the service responds with a + // `continue` status. This is useful to prevent sending the request body + // until after the request is authenticated, and validated. + // + // http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html + // + // 100-Continue is only enabled for Go 1.6 and above. See `http.Transport`'s + // `ExpectContinueTimeout` for information on adjusting the continue wait timeout. + // https://golang.org/pkg/net/http/#Transport + // + // You should use this flag to disble 100-Continue if you experiance issues + // with proxies or thrid party S3 compatible services. + S3Disable100Continue *bool + + // Set this to `true` to enable S3 Accelerate feature. For all operations compatible + // with S3 Accelerate will use the accelerate endpoint for requests. Requests not compatible + // will fall back to normal S3 requests. + // + // The bucket must be enable for accelerate to be used with S3 client with accelerate + // enabled. If the bucket is not enabled for accelerate an error will be returned. + // The bucket name must be DNS compatible to also work with accelerate. + S3UseAccelerate *bool + // Set this to `true` to disable the EC2Metadata client from overriding the // default http.Client's Timeout. This is helpful if you do not want the EC2Metadata // client to create a new http.Client. This options is only meaningful if you're not @@ -210,6 +235,20 @@ func (c *Config) WithS3ForcePathStyle(force bool) *Config { return c } +// WithS3Disable100Continue sets a config S3Disable100Continue value returning +// a Config pointer for chaining. +func (c *Config) WithS3Disable100Continue(disable bool) *Config { + c.S3Disable100Continue = &disable + return c +} + +// WithS3UseAccelerate sets a config S3UseAccelerate value returning a Config +// pointer for chaining. +func (c *Config) WithS3UseAccelerate(enable bool) *Config { + c.S3UseAccelerate = &enable + return c +} + // WithEC2MetadataDisableTimeoutOverride sets a config EC2MetadataDisableTimeoutOverride value // returning a Config pointer for chaining. func (c *Config) WithEC2MetadataDisableTimeoutOverride(enable bool) *Config { @@ -288,6 +327,14 @@ func mergeInConfig(dst *Config, other *Config) { dst.S3ForcePathStyle = other.S3ForcePathStyle } + if other.S3Disable100Continue != nil { + dst.S3Disable100Continue = other.S3Disable100Continue + } + + if other.S3UseAccelerate != nil { + dst.S3UseAccelerate = other.S3UseAccelerate + } + if other.EC2MetadataDisableTimeoutOverride != nil { dst.EC2MetadataDisableTimeoutOverride = other.EC2MetadataDisableTimeoutOverride } diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go index a054d39..8456e29 100644 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go @@ -24,30 +24,38 @@ type lener interface { // BuildContentLengthHandler builds the content length of a request based on the body, // or will use the HTTPRequest.Header's "Content-Length" if defined. If unable // to determine request body length and no "Content-Length" was specified it will panic. +// +// The Content-Length will only be aded to the request if the length of the body +// is greater than 0. If the body is empty or the current `Content-Length` +// header is <= 0, the header will also be stripped. var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLengthHandler", Fn: func(r *request.Request) { - if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" { - length, _ := strconv.ParseInt(slength, 10, 64) - r.HTTPRequest.ContentLength = length - return - } - var length int64 - switch body := r.Body.(type) { - case nil: - length = 0 - case lener: - length = int64(body.Len()) - case io.Seeker: - r.BodyStart, _ = body.Seek(0, 1) - end, _ := body.Seek(0, 2) - body.Seek(r.BodyStart, 0) // make sure to seek back to original location - length = end - r.BodyStart - default: - panic("Cannot get length of body, must provide `ContentLength`") + + if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" { + length, _ = strconv.ParseInt(slength, 10, 64) + } else { + switch body := r.Body.(type) { + case nil: + length = 0 + case lener: + length = int64(body.Len()) + case io.Seeker: + r.BodyStart, _ = body.Seek(0, 1) + end, _ := body.Seek(0, 2) + body.Seek(r.BodyStart, 0) // make sure to seek back to original location + length = end - r.BodyStart + default: + panic("Cannot get length of body, must provide `ContentLength`") + } } - r.HTTPRequest.ContentLength = length - r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length)) + if length > 0 { + r.HTTPRequest.ContentLength = length + r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length)) + } else { + r.HTTPRequest.ContentLength = 0 + r.HTTPRequest.Header.Del("Content-Length") + } }} // SDKVersionUserAgentHandler is a request handler for adding the SDK Version to the user agent. diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/param_validator.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/param_validator.go index ea07580..7d50b15 100644 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/param_validator.go +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/corehandlers/param_validator.go @@ -1,153 +1,17 @@ package corehandlers -import ( - "fmt" - "reflect" - "strconv" - "strings" - - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/request" -) +import "github.com/aws/aws-sdk-go/aws/request" // ValidateParametersHandler is a request handler to validate the input parameters. // Validating parameters only has meaning if done prior to the request being sent. var ValidateParametersHandler = request.NamedHandler{Name: "core.ValidateParametersHandler", Fn: func(r *request.Request) { - if r.ParamsFilled() { - v := validator{errors: []string{}} - v.validateAny(reflect.ValueOf(r.Params), "") - - if count := len(v.errors); count > 0 { - format := "%d validation errors:\n- %s" - msg := fmt.Sprintf(format, count, strings.Join(v.errors, "\n- ")) - r.Error = awserr.New("InvalidParameter", msg, nil) - } - } -}} - -// A validator validates values. Collects validations errors which occurs. -type validator struct { - errors []string -} - -// There's no validation to be done on the contents of []byte values. Prepare -// to check validateAny arguments against that type so we can quickly skip -// them. -var byteSliceType = reflect.TypeOf([]byte(nil)) - -// validateAny will validate any struct, slice or map type. All validations -// are also performed recursively for nested types. -func (v *validator) validateAny(value reflect.Value, path string) { - value = reflect.Indirect(value) - if !value.IsValid() { + if !r.ParamsFilled() { return } - switch value.Kind() { - case reflect.Struct: - v.validateStruct(value, path) - case reflect.Slice: - if value.Type() == byteSliceType { - // We don't need to validate the contents of []byte. - return - } - for i := 0; i < value.Len(); i++ { - v.validateAny(value.Index(i), path+fmt.Sprintf("[%d]", i)) - } - case reflect.Map: - for _, n := range value.MapKeys() { - v.validateAny(value.MapIndex(n), path+fmt.Sprintf("[%q]", n.String())) + if v, ok := r.Params.(request.Validator); ok { + if err := v.Validate(); err != nil { + r.Error = err } } -} - -// validateStruct will validate the struct value's fields. If the structure has -// nested types those types will be validated also. -func (v *validator) validateStruct(value reflect.Value, path string) { - prefix := "." - if path == "" { - prefix = "" - } - - for i := 0; i < value.Type().NumField(); i++ { - f := value.Type().Field(i) - if strings.ToLower(f.Name[0:1]) == f.Name[0:1] { - continue - } - fvalue := value.FieldByName(f.Name) - - err := validateField(f, fvalue, validateFieldRequired, validateFieldMin) - if err != nil { - v.errors = append(v.errors, fmt.Sprintf("%s: %s", err.Error(), path+prefix+f.Name)) - continue - } - - v.validateAny(fvalue, path+prefix+f.Name) - } -} - -type validatorFunc func(f reflect.StructField, fvalue reflect.Value) error - -func validateField(f reflect.StructField, fvalue reflect.Value, funcs ...validatorFunc) error { - for _, fn := range funcs { - if err := fn(f, fvalue); err != nil { - return err - } - } - return nil -} - -// Validates that a field has a valid value provided for required fields. -func validateFieldRequired(f reflect.StructField, fvalue reflect.Value) error { - if f.Tag.Get("required") == "" { - return nil - } - - switch fvalue.Kind() { - case reflect.Ptr, reflect.Slice, reflect.Map: - if fvalue.IsNil() { - return fmt.Errorf("missing required parameter") - } - default: - if !fvalue.IsValid() { - return fmt.Errorf("missing required parameter") - } - } - return nil -} - -// Validates that if a value is provided for a field, that value must be at -// least a minimum length. -func validateFieldMin(f reflect.StructField, fvalue reflect.Value) error { - minStr := f.Tag.Get("min") - if minStr == "" { - return nil - } - min, _ := strconv.ParseInt(minStr, 10, 64) - - kind := fvalue.Kind() - if kind == reflect.Ptr { - if fvalue.IsNil() { - return nil - } - fvalue = fvalue.Elem() - } - - switch fvalue.Kind() { - case reflect.String: - if int64(fvalue.Len()) < min { - return fmt.Errorf("field too short, minimum length %d", min) - } - case reflect.Slice, reflect.Map: - if fvalue.IsNil() { - return nil - } - if int64(fvalue.Len()) < min { - return fmt.Errorf("field too short, minimum length %d", min) - } - - // TODO min can also apply to number minimum value. - - } - return nil -} +}} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go index 71189e7..6f07560 100644 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/credentials/static_provider.go @@ -14,7 +14,7 @@ var ( ErrStaticCredentialsEmpty = awserr.New("EmptyStaticCreds", "static credentials are empty", nil) ) -// A StaticProvider is a set of credentials which are set pragmatically, +// A StaticProvider is a set of credentials which are set programmatically, // and will never expire. type StaticProvider struct { Value diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/http_request.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/http_request.go new file mode 100644 index 0000000..3127762 --- /dev/null +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/http_request.go @@ -0,0 +1,26 @@ +// +build go1.5 + +package request + +import ( + "io" + "net/http" +) + +func copyHTTPRequest(r *http.Request, body io.ReadCloser) *http.Request { + return &http.Request{ + URL: r.URL, + Header: r.Header, + Close: r.Close, + Form: r.Form, + PostForm: r.PostForm, + Body: body, + MultipartForm: r.MultipartForm, + Host: r.Host, + Method: r.Method, + Proto: r.Proto, + ContentLength: r.ContentLength, + // Cancel will be deprecated in 1.7 and will be replaced with Context + Cancel: r.Cancel, + } +} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/http_request_1_4.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/http_request_1_4.go new file mode 100644 index 0000000..86c9c2f --- /dev/null +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/http_request_1_4.go @@ -0,0 +1,24 @@ +// +build !go1.5 + +package request + +import ( + "io" + "net/http" +) + +func copyHTTPRequest(r *http.Request, body io.ReadCloser) *http.Request { + return &http.Request{ + URL: r.URL, + Header: r.Header, + Close: r.Close, + Form: r.Form, + PostForm: r.PostForm, + Body: body, + MultipartForm: r.MultipartForm, + Host: r.Host, + Method: r.Method, + Proto: r.Proto, + ContentLength: r.ContentLength, + } +} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/request.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/request.go index a4b7054..2391632 100644 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/request.go +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/request.go @@ -221,6 +221,13 @@ func (r *Request) Sign() error { // // Send will sign the request prior to sending. All Send Handlers will // be executed in the order they were set. +// +// Canceling a request is non-deterministic. If a request has been canceled, +// then the transport will choose, randomly, one of the state channels during +// reads or getting the connection. +// +// readLoop() and getConn(req *Request, cm connectMethod) +// https://github.com/golang/go/blob/master/src/net/http/transport.go func (r *Request) Send() error { for { if aws.BoolValue(r.Retryable) { @@ -240,22 +247,12 @@ func (r *Request) Send() error { body = ioutil.NopCloser(r.Body) } - r.HTTPRequest = &http.Request{ - URL: r.HTTPRequest.URL, - Header: r.HTTPRequest.Header, - Close: r.HTTPRequest.Close, - Form: r.HTTPRequest.Form, - PostForm: r.HTTPRequest.PostForm, - Body: body, - MultipartForm: r.HTTPRequest.MultipartForm, - Host: r.HTTPRequest.Host, - Method: r.HTTPRequest.Method, - Proto: r.HTTPRequest.Proto, - ContentLength: r.HTTPRequest.ContentLength, + r.HTTPRequest = copyHTTPRequest(r.HTTPRequest, body) + if r.HTTPResponse != nil && r.HTTPResponse.Body != nil { + // Closing response body. Since we are setting a new request to send off, this + // response will get squashed and leaked. + r.HTTPResponse.Body.Close() } - // Closing response body. Since we are setting a new request to send off, this - // response will get squashed and leaked. - r.HTTPResponse.Body.Close() } r.Sign() @@ -267,6 +264,10 @@ func (r *Request) Send() error { r.Handlers.Send.Run(r) if r.Error != nil { + if strings.Contains(r.Error.Error(), "net/http: request canceled") { + return r.Error + } + err := r.Error r.Handlers.Retry.Run(r) r.Handlers.AfterRetry.Run(r) diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/validation.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/validation.go new file mode 100644 index 0000000..2520286 --- /dev/null +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/request/validation.go @@ -0,0 +1,234 @@ +package request + +import ( + "bytes" + "fmt" + + "github.com/aws/aws-sdk-go/aws/awserr" +) + +const ( + // InvalidParameterErrCode is the error code for invalid parameters errors + InvalidParameterErrCode = "InvalidParameter" + // ParamRequiredErrCode is the error code for required parameter errors + ParamRequiredErrCode = "ParamRequiredError" + // ParamMinValueErrCode is the error code for fields with too low of a + // number value. + ParamMinValueErrCode = "ParamMinValueError" + // ParamMinLenErrCode is the error code for fields without enough elements. + ParamMinLenErrCode = "ParamMinLenError" +) + +// Validator provides a way for types to perform validation logic on their +// input values that external code can use to determine if a type's values +// are valid. +type Validator interface { + Validate() error +} + +// An ErrInvalidParams provides wrapping of invalid parameter errors found when +// validating API operation input parameters. +type ErrInvalidParams struct { + // Context is the base context of the invalid parameter group. + Context string + errs []ErrInvalidParam +} + +// Add adds a new invalid parameter error to the collection of invalid +// parameters. The context of the invalid parameter will be updated to reflect +// this collection. +func (e *ErrInvalidParams) Add(err ErrInvalidParam) { + err.SetContext(e.Context) + e.errs = append(e.errs, err) +} + +// AddNested adds the invalid parameter errors from another ErrInvalidParams +// value into this collection. The nested errors will have their nested context +// updated and base context to reflect the merging. +// +// Use for nested validations errors. +func (e *ErrInvalidParams) AddNested(nestedCtx string, nested ErrInvalidParams) { + for _, err := range nested.errs { + err.SetContext(e.Context) + err.AddNestedContext(nestedCtx) + e.errs = append(e.errs, err) + } +} + +// Len returns the number of invalid parameter errors +func (e ErrInvalidParams) Len() int { + return len(e.errs) +} + +// Code returns the code of the error +func (e ErrInvalidParams) Code() string { + return InvalidParameterErrCode +} + +// Message returns the message of the error +func (e ErrInvalidParams) Message() string { + return fmt.Sprintf("%d validation error(s) found.", len(e.errs)) +} + +// Error returns the string formatted form of the invalid parameters. +func (e ErrInvalidParams) Error() string { + w := &bytes.Buffer{} + fmt.Fprintf(w, "%s: %s\n", e.Code(), e.Message()) + + for _, err := range e.errs { + fmt.Fprintf(w, "- %s\n", err.Message()) + } + + return w.String() +} + +// OrigErr returns the invalid parameters as a awserr.BatchedErrors value +func (e ErrInvalidParams) OrigErr() error { + return awserr.NewBatchError( + InvalidParameterErrCode, e.Message(), e.OrigErrs()) +} + +// OrigErrs returns a slice of the invalid parameters +func (e ErrInvalidParams) OrigErrs() []error { + errs := make([]error, len(e.errs)) + for i := 0; i < len(errs); i++ { + errs[i] = e.errs[i] + } + + return errs +} + +// An ErrInvalidParam represents an invalid parameter error type. +type ErrInvalidParam interface { + awserr.Error + + // Field name the error occurred on. + Field() string + + // SetContext updates the context of the error. + SetContext(string) + + // AddNestedContext updates the error's context to include a nested level. + AddNestedContext(string) +} + +type errInvalidParam struct { + context string + nestedContext string + field string + code string + msg string +} + +// Code returns the error code for the type of invalid parameter. +func (e *errInvalidParam) Code() string { + return e.code +} + +// Message returns the reason the parameter was invalid, and its context. +func (e *errInvalidParam) Message() string { + return fmt.Sprintf("%s, %s.", e.msg, e.Field()) +} + +// Error returns the string version of the invalid parameter error. +func (e *errInvalidParam) Error() string { + return fmt.Sprintf("%s: %s", e.code, e.Message()) +} + +// OrigErr returns nil, Implemented for awserr.Error interface. +func (e *errInvalidParam) OrigErr() error { + return nil +} + +// Field Returns the field and context the error occurred. +func (e *errInvalidParam) Field() string { + field := e.context + if len(field) > 0 { + field += "." + } + if len(e.nestedContext) > 0 { + field += fmt.Sprintf("%s.", e.nestedContext) + } + field += e.field + + return field +} + +// SetContext updates the base context of the error. +func (e *errInvalidParam) SetContext(ctx string) { + e.context = ctx +} + +// AddNestedContext prepends a context to the field's path. +func (e *errInvalidParam) AddNestedContext(ctx string) { + if len(e.nestedContext) == 0 { + e.nestedContext = ctx + } else { + e.nestedContext = fmt.Sprintf("%s.%s", ctx, e.nestedContext) + } + +} + +// An ErrParamRequired represents an required parameter error. +type ErrParamRequired struct { + errInvalidParam +} + +// NewErrParamRequired creates a new required parameter error. +func NewErrParamRequired(field string) *ErrParamRequired { + return &ErrParamRequired{ + errInvalidParam{ + code: ParamRequiredErrCode, + field: field, + msg: fmt.Sprintf("missing required field"), + }, + } +} + +// An ErrParamMinValue represents a minimum value parameter error. +type ErrParamMinValue struct { + errInvalidParam + min float64 +} + +// NewErrParamMinValue creates a new minimum value parameter error. +func NewErrParamMinValue(field string, min float64) *ErrParamMinValue { + return &ErrParamMinValue{ + errInvalidParam: errInvalidParam{ + code: ParamMinValueErrCode, + field: field, + msg: fmt.Sprintf("minimum field value of %v", min), + }, + min: min, + } +} + +// MinValue returns the field's require minimum value. +// +// float64 is returned for both int and float min values. +func (e *ErrParamMinValue) MinValue() float64 { + return e.min +} + +// An ErrParamMinLen represents a minimum length parameter error. +type ErrParamMinLen struct { + errInvalidParam + min int +} + +// NewErrParamMinLen creates a new minimum length parameter error. +func NewErrParamMinLen(field string, min int) *ErrParamMinLen { + return &ErrParamMinLen{ + errInvalidParam: errInvalidParam{ + code: ParamMinValueErrCode, + field: field, + msg: fmt.Sprintf("minimum field size of %v", min), + }, + min: min, + } +} + +// MinLen returns the field's required minimum length. +func (e *ErrParamMinLen) MinLen() int { + return e.min +} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/session/session.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/session/session.go index 47e4536..6bc8f1b 100644 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/session/session.go +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/session/session.go @@ -86,7 +86,7 @@ func initHandlers(s *Session) { // // Example: // // Create a copy of the current session, configured for the us-west-2 region. -// sess.Copy(&aws.Config{Region: aws.String("us-west-2"}) +// sess.Copy(&aws.Config{Region: aws.String("us-west-2")}) func (s *Session) Copy(cfgs ...*aws.Config) *Session { newSession := &Session{ Config: s.Config.Copy(cfgs...), diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/version.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/version.go index 805dc71..79067e0 100644 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/version.go +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.1.15" +const SDKVersion = "1.1.25" diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/signer/v4/v4.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/signer/v4/v4.go index 14b0c66..4765800 100644 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/signer/v4/v4.go +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/private/signer/v4/v4.go @@ -29,8 +29,8 @@ const ( var ignoredHeaders = rules{ blacklist{ mapRule{ - "Content-Length": struct{}{}, - "User-Agent": struct{}{}, + "Authorization": struct{}{}, + "User-Agent": struct{}{}, }, }, } @@ -153,6 +153,7 @@ func Sign(req *request.Request) { } req.Error = s.sign() + req.Time = s.Time req.SignedHeaderVals = s.signedHeaderVals } @@ -162,11 +163,12 @@ func (v4 *signer) sign() error { } if v4.isRequestSigned() { - if !v4.Credentials.IsExpired() { + if !v4.Credentials.IsExpired() && time.Now().Before(v4.Time.Add(10*time.Minute)) { // If the request is already signed, and the credentials have not - // expired yet ignore the signing request. + // expired, and the request is not too old ignore the signing request. return nil } + v4.Time = time.Now() // The credentials have expired for this request. The current signing // is invalid, and needs to be request because the request will fail. @@ -303,13 +305,18 @@ func (v4 *signer) buildCanonicalHeaders(r rule, header http.Header) { if !r.IsValid(canonicalKey) { continue // ignored header } - - lowerCaseKey := strings.ToLower(k) - headers = append(headers, lowerCaseKey) - if v4.signedHeaderVals == nil { v4.signedHeaderVals = make(http.Header) } + + lowerCaseKey := strings.ToLower(k) + if _, ok := v4.signedHeaderVals[lowerCaseKey]; ok { + // include additional values + v4.signedHeaderVals[lowerCaseKey] = append(v4.signedHeaderVals[lowerCaseKey], v...) + continue + } + + headers = append(headers, lowerCaseKey) v4.signedHeaderVals[lowerCaseKey] = v } sort.Strings(headers) @@ -326,11 +333,11 @@ func (v4 *signer) buildCanonicalHeaders(r rule, header http.Header) { headerValues[i] = "host:" + v4.Request.URL.Host } else { headerValues[i] = k + ":" + - strings.Join(v4.Request.Header[http.CanonicalHeaderKey(k)], ",") + strings.Join(v4.signedHeaderVals[k], ",") } } - v4.canonicalHeaders = strings.Join(headerValues, "\n") + v4.canonicalHeaders = strings.Join(stripExcessSpaces(headerValues), "\n") } func (v4 *signer) buildCanonicalString() { @@ -436,3 +443,23 @@ func makeSha256Reader(reader io.ReadSeeker) []byte { io.Copy(hash, reader) return hash.Sum(nil) } + +func stripExcessSpaces(headerVals []string) []string { + vals := make([]string, len(headerVals)) + for i, str := range headerVals { + stripped := "" + found := false + str = strings.TrimSpace(str) + for _, c := range str { + if !found && c == ' ' { + stripped += string(c) + found = true + } else if c != ' ' { + stripped += string(c) + found = false + } + } + vals[i] = stripped + } + return vals +} diff --git a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/route53/api.go b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/route53/api.go index 04747ea..f0c3133 100644 --- a/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/route53/api.go +++ b/Godeps/_workspace/src/github.com/aws/aws-sdk-go/service/route53/api.go @@ -4,6 +4,7 @@ package route53 import ( + "fmt" "time" "github.com/aws/aws-sdk-go/aws/awsutil" @@ -1809,6 +1810,50 @@ func (c *Route53) UpdateTrafficPolicyInstance(input *UpdateTrafficPolicyInstance return out, err } +// A complex type that contains information to uniquely identify the CloudWatch +// alarm that you're associating with a Route 53 health check. +type AlarmIdentifier struct { + _ struct{} `type:"structure"` + + // The name of the CloudWatch alarm. + Name *string `min:"1" type:"string" required:"true"` + + // The CloudWatchRegion that the CloudWatch alarm was created in. + Region *string `min:"1" type:"string" required:"true" enum:"CloudWatchRegion"` +} + +// String returns the string representation +func (s AlarmIdentifier) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s AlarmIdentifier) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *AlarmIdentifier) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "AlarmIdentifier"} + if s.Name == nil { + invalidParams.Add(request.NewErrParamRequired("Name")) + } + if s.Name != nil && len(*s.Name) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Name", 1)) + } + if s.Region == nil { + invalidParams.Add(request.NewErrParamRequired("Region")) + } + if s.Region != nil && len(*s.Region) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Region", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // Alias resource record sets only: Information about the CloudFront distribution, // ELB load balancer, Amazon S3 bucket, or Amazon Route 53 resource record set // to which you are routing traffic. @@ -1944,6 +1989,25 @@ func (s AliasTarget) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *AliasTarget) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "AliasTarget"} + if s.DNSName == nil { + invalidParams.Add(request.NewErrParamRequired("DNSName")) + } + if s.EvaluateTargetHealth == nil { + invalidParams.Add(request.NewErrParamRequired("EvaluateTargetHealth")) + } + if s.HostedZoneId == nil { + invalidParams.Add(request.NewErrParamRequired("HostedZoneId")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the request to associate a // VPC with an hosted zone. type AssociateVPCWithHostedZoneInput struct { @@ -1972,6 +2036,27 @@ func (s AssociateVPCWithHostedZoneInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *AssociateVPCWithHostedZoneInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "AssociateVPCWithHostedZoneInput"} + if s.HostedZoneId == nil { + invalidParams.Add(request.NewErrParamRequired("HostedZoneId")) + } + if s.VPC == nil { + invalidParams.Add(request.NewErrParamRequired("VPC")) + } + if s.VPC != nil { + if err := s.VPC.Validate(); err != nil { + invalidParams.AddNested("VPC", err.(request.ErrInvalidParams)) + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing the response information for the request. type AssociateVPCWithHostedZoneOutput struct { _ struct{} `type:"structure"` @@ -2025,6 +2110,27 @@ func (s Change) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *Change) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "Change"} + if s.Action == nil { + invalidParams.Add(request.NewErrParamRequired("Action")) + } + if s.ResourceRecordSet == nil { + invalidParams.Add(request.NewErrParamRequired("ResourceRecordSet")) + } + if s.ResourceRecordSet != nil { + if err := s.ResourceRecordSet.Validate(); err != nil { + invalidParams.AddNested("ResourceRecordSet", err.(request.ErrInvalidParams)) + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains an optional comment and the changes that you // want to make with a change batch request. type ChangeBatch struct { @@ -2048,6 +2154,32 @@ func (s ChangeBatch) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ChangeBatch) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ChangeBatch"} + if s.Changes == nil { + invalidParams.Add(request.NewErrParamRequired("Changes")) + } + if s.Changes != nil && len(s.Changes) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Changes", 1)) + } + if s.Changes != nil { + for i, v := range s.Changes { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "Changes", i), err.(request.ErrInvalidParams)) + } + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that lists the changes and information for a ChangeBatch. type ChangeBatchRecord struct { _ struct{} `deprecated:"true" type:"structure"` @@ -2156,6 +2288,27 @@ func (s ChangeResourceRecordSetsInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ChangeResourceRecordSetsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ChangeResourceRecordSetsInput"} + if s.ChangeBatch == nil { + invalidParams.Add(request.NewErrParamRequired("ChangeBatch")) + } + if s.HostedZoneId == nil { + invalidParams.Add(request.NewErrParamRequired("HostedZoneId")) + } + if s.ChangeBatch != nil { + if err := s.ChangeBatch.Validate(); err != nil { + invalidParams.AddNested("ChangeBatch", err.(request.ErrInvalidParams)) + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing the response for the request. type ChangeResourceRecordSetsOutput struct { _ struct{} `type:"structure"` @@ -2211,6 +2364,28 @@ func (s ChangeTagsForResourceInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ChangeTagsForResourceInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ChangeTagsForResourceInput"} + if s.AddTags != nil && len(s.AddTags) < 1 { + invalidParams.Add(request.NewErrParamMinLen("AddTags", 1)) + } + if s.RemoveTagKeys != nil && len(s.RemoveTagKeys) < 1 { + invalidParams.Add(request.NewErrParamMinLen("RemoveTagKeys", 1)) + } + if s.ResourceId == nil { + invalidParams.Add(request.NewErrParamRequired("ResourceId")) + } + if s.ResourceType == nil { + invalidParams.Add(request.NewErrParamRequired("ResourceType")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // Empty response for the request. type ChangeTagsForResourceOutput struct { _ struct{} `type:"structure"` @@ -2226,6 +2401,62 @@ func (s ChangeTagsForResourceOutput) GoString() string { return s.String() } +// For CLOUDWATCH_METRIC health checks, a complex type that contains information +// about the CloudWatch alarm that you're associating with the health check. +type CloudWatchAlarmConfiguration struct { + _ struct{} `type:"structure"` + + // The arithmetic operation to use when comparing the specified Statistic and + // Threshold. + // + // Valid Values are GreaterThanOrEqualToThreshold, GreaterThanThreshold, LessThanThreshold + // and LessThanOrEqualToThreshold + ComparisonOperator *string `type:"string" required:"true" enum:"ComparisonOperator"` + + // A list of Dimension elements for the CloudWatch metric that is associated + // with the CloudWatch alarm. For information about the metrics and dimensions + // that CloudWatch supports, see Amazon CloudWatch Namespaces, Dimensions, and + // Metrics Reference (http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html). + Dimensions []*Dimension `locationNameList:"Dimension" type:"list"` + + // The number of periods over which data is compared to the specified threshold. + EvaluationPeriods *int64 `min:"1" type:"integer" required:"true"` + + // The name of the CloudWatch metric that is associated with the CloudWatch + // alarm. + MetricName *string `min:"1" type:"string" required:"true"` + + // The namespace of the CloudWatch metric that is associated with the CloudWatch + // alarm. + Namespace *string `min:"1" type:"string" required:"true"` + + // An integer that represents the period in seconds over which the statistic + // is applied. + Period *int64 `min:"60" type:"integer" required:"true"` + + // The statistic to apply to the CloudWatch metric that is associated with the + // CloudWatch alarm. + // + // Valid Values are SampleCount, Average, Sum, Minimum and Maximum + Statistic *string `type:"string" required:"true" enum:"Statistic"` + + // The value that the metric is compared with to determine the state of the + // alarm. For example, if you want the health check to fail if the average TCP + // connection time is greater than 500 milliseconds for more than 60 seconds, + // the threshold is 500. + Threshold *float64 `type:"double" required:"true"` +} + +// String returns the string representation +func (s CloudWatchAlarmConfiguration) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CloudWatchAlarmConfiguration) GoString() string { + return s.String() +} + // >A complex type that contains information about the request to create a health // check. type CreateHealthCheckInput struct { @@ -2255,6 +2486,30 @@ func (s CreateHealthCheckInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *CreateHealthCheckInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CreateHealthCheckInput"} + if s.CallerReference == nil { + invalidParams.Add(request.NewErrParamRequired("CallerReference")) + } + if s.CallerReference != nil && len(*s.CallerReference) < 1 { + invalidParams.Add(request.NewErrParamMinLen("CallerReference", 1)) + } + if s.HealthCheckConfig == nil { + invalidParams.Add(request.NewErrParamRequired("HealthCheckConfig")) + } + if s.HealthCheckConfig != nil { + if err := s.HealthCheckConfig.Validate(); err != nil { + invalidParams.AddNested("HealthCheckConfig", err.(request.ErrInvalidParams)) + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing the response information for the new health check. type CreateHealthCheckOutput struct { _ struct{} `type:"structure"` @@ -2325,6 +2580,30 @@ func (s CreateHostedZoneInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *CreateHostedZoneInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CreateHostedZoneInput"} + if s.CallerReference == nil { + invalidParams.Add(request.NewErrParamRequired("CallerReference")) + } + if s.CallerReference != nil && len(*s.CallerReference) < 1 { + invalidParams.Add(request.NewErrParamMinLen("CallerReference", 1)) + } + if s.Name == nil { + invalidParams.Add(request.NewErrParamRequired("Name")) + } + if s.VPC != nil { + if err := s.VPC.Validate(); err != nil { + invalidParams.AddNested("VPC", err.(request.ErrInvalidParams)) + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing the response information for the new hosted zone. type CreateHostedZoneOutput struct { _ struct{} `type:"structure"` @@ -2384,6 +2663,22 @@ func (s CreateReusableDelegationSetInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *CreateReusableDelegationSetInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CreateReusableDelegationSetInput"} + if s.CallerReference == nil { + invalidParams.Add(request.NewErrParamRequired("CallerReference")) + } + if s.CallerReference != nil && len(*s.CallerReference) < 1 { + invalidParams.Add(request.NewErrParamMinLen("CallerReference", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + type CreateReusableDelegationSetOutput struct { _ struct{} `type:"structure"` @@ -2429,6 +2724,22 @@ func (s CreateTrafficPolicyInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *CreateTrafficPolicyInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CreateTrafficPolicyInput"} + if s.Document == nil { + invalidParams.Add(request.NewErrParamRequired("Document")) + } + if s.Name == nil { + invalidParams.Add(request.NewErrParamRequired("Name")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the resource record sets that // you want to create based on a specified traffic policy. type CreateTrafficPolicyInstanceInput struct { @@ -2466,6 +2777,34 @@ func (s CreateTrafficPolicyInstanceInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *CreateTrafficPolicyInstanceInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CreateTrafficPolicyInstanceInput"} + if s.HostedZoneId == nil { + invalidParams.Add(request.NewErrParamRequired("HostedZoneId")) + } + if s.Name == nil { + invalidParams.Add(request.NewErrParamRequired("Name")) + } + if s.TTL == nil { + invalidParams.Add(request.NewErrParamRequired("TTL")) + } + if s.TrafficPolicyId == nil { + invalidParams.Add(request.NewErrParamRequired("TrafficPolicyId")) + } + if s.TrafficPolicyVersion == nil { + invalidParams.Add(request.NewErrParamRequired("TrafficPolicyVersion")) + } + if s.TrafficPolicyVersion != nil && *s.TrafficPolicyVersion < 1 { + invalidParams.Add(request.NewErrParamMinValue("TrafficPolicyVersion", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains the response information for the CreateTrafficPolicyInstance // request. type CreateTrafficPolicyInstanceOutput struct { @@ -2536,6 +2875,22 @@ func (s CreateTrafficPolicyVersionInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *CreateTrafficPolicyVersionInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "CreateTrafficPolicyVersionInput"} + if s.Document == nil { + invalidParams.Add(request.NewErrParamRequired("Document")) + } + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains the response information for the CreateTrafficPolicyVersion // request. type CreateTrafficPolicyVersionOutput struct { @@ -2600,6 +2955,19 @@ func (s DeleteHealthCheckInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteHealthCheckInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteHealthCheckInput"} + if s.HealthCheckId == nil { + invalidParams.Add(request.NewErrParamRequired("HealthCheckId")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // Empty response for the request. type DeleteHealthCheckOutput struct { _ struct{} `type:"structure"` @@ -2634,6 +3002,19 @@ func (s DeleteHostedZoneInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteHostedZoneInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteHostedZoneInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing the response information for the request. type DeleteHostedZoneOutput struct { _ struct{} `type:"structure"` @@ -2671,6 +3052,19 @@ func (s DeleteReusableDelegationSetInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteReusableDelegationSetInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteReusableDelegationSetInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // Empty response for the request. type DeleteReusableDelegationSetOutput struct { _ struct{} `type:"structure"` @@ -2707,6 +3101,25 @@ func (s DeleteTrafficPolicyInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteTrafficPolicyInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteTrafficPolicyInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + if s.Version == nil { + invalidParams.Add(request.NewErrParamRequired("Version")) + } + if s.Version != nil && *s.Version < 1 { + invalidParams.Add(request.NewErrParamMinValue("Version", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the traffic policy instance // that you want to delete. type DeleteTrafficPolicyInstanceInput struct { @@ -2730,6 +3143,19 @@ func (s DeleteTrafficPolicyInstanceInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *DeleteTrafficPolicyInstanceInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DeleteTrafficPolicyInstanceInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // An empty element. type DeleteTrafficPolicyInstanceOutput struct { _ struct{} `type:"structure"` @@ -2760,6 +3186,27 @@ func (s DeleteTrafficPolicyOutput) GoString() string { return s.String() } +// The name and value of a dimension for a CloudWatch metric. +type Dimension struct { + _ struct{} `type:"structure"` + + // The name of the dimension. + Name *string `min:"1" type:"string" required:"true"` + + // The value of the dimension. + Value *string `min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s Dimension) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s Dimension) GoString() string { + return s.String() +} + // A complex type that contains information about the request to disassociate // a VPC from an hosted zone. type DisassociateVPCFromHostedZoneInput struct { @@ -2787,6 +3234,27 @@ func (s DisassociateVPCFromHostedZoneInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *DisassociateVPCFromHostedZoneInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "DisassociateVPCFromHostedZoneInput"} + if s.HostedZoneId == nil { + invalidParams.Add(request.NewErrParamRequired("HostedZoneId")) + } + if s.VPC == nil { + invalidParams.Add(request.NewErrParamRequired("VPC")) + } + if s.VPC != nil { + if err := s.VPC.Validate(); err != nil { + invalidParams.AddNested("VPC", err.(request.ErrInvalidParams)) + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing the response information for the request. type DisassociateVPCFromHostedZoneOutput struct { _ struct{} `type:"structure"` @@ -2844,6 +3312,25 @@ func (s GeoLocation) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GeoLocation) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GeoLocation"} + if s.ContinentCode != nil && len(*s.ContinentCode) < 2 { + invalidParams.Add(request.NewErrParamMinLen("ContinentCode", 2)) + } + if s.CountryCode != nil && len(*s.CountryCode) < 1 { + invalidParams.Add(request.NewErrParamMinLen("CountryCode", 1)) + } + if s.SubdivisionCode != nil && len(*s.SubdivisionCode) < 1 { + invalidParams.Add(request.NewErrParamMinLen("SubdivisionCode", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about a GeoLocation. type GeoLocationDetails struct { _ struct{} `type:"structure"` @@ -2906,6 +3393,19 @@ func (s GetChangeDetailsInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetChangeDetailsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetChangeDetailsInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains the ChangeBatchRecord element. type GetChangeDetailsOutput struct { _ struct{} `deprecated:"true" type:"structure"` @@ -2946,6 +3446,19 @@ func (s GetChangeInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetChangeInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetChangeInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains the ChangeInfo element. type GetChangeOutput struct { _ struct{} `type:"structure"` @@ -3038,6 +3551,25 @@ func (s GetGeoLocationInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetGeoLocationInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetGeoLocationInput"} + if s.ContinentCode != nil && len(*s.ContinentCode) < 2 { + invalidParams.Add(request.NewErrParamMinLen("ContinentCode", 2)) + } + if s.CountryCode != nil && len(*s.CountryCode) < 1 { + invalidParams.Add(request.NewErrParamMinLen("CountryCode", 1)) + } + if s.SubdivisionCode != nil && len(*s.SubdivisionCode) < 1 { + invalidParams.Add(request.NewErrParamMinLen("SubdivisionCode", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing information about the specified geo location. type GetGeoLocationOutput struct { _ struct{} `type:"structure"` @@ -3110,6 +3642,19 @@ func (s GetHealthCheckInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetHealthCheckInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetHealthCheckInput"} + if s.HealthCheckId == nil { + invalidParams.Add(request.NewErrParamRequired("HealthCheckId")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the request to get the most // recent failure reason for a health check. type GetHealthCheckLastFailureReasonInput struct { @@ -3130,6 +3675,19 @@ func (s GetHealthCheckLastFailureReasonInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetHealthCheckLastFailureReasonInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetHealthCheckLastFailureReasonInput"} + if s.HealthCheckId == nil { + invalidParams.Add(request.NewErrParamRequired("HealthCheckId")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the most recent failure for // the specified health check. type GetHealthCheckLastFailureReasonOutput struct { @@ -3241,6 +3799,19 @@ func (s GetHealthCheckStatusInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetHealthCheckStatusInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetHealthCheckStatusInput"} + if s.HealthCheckId == nil { + invalidParams.Add(request.NewErrParamRequired("HealthCheckId")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the status of the specified // health check. type GetHealthCheckStatusOutput struct { @@ -3315,6 +3886,19 @@ func (s GetHostedZoneInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetHostedZoneInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetHostedZoneInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing information about the specified hosted zone. type GetHostedZoneOutput struct { _ struct{} `type:"structure"` @@ -3360,6 +3944,19 @@ func (s GetReusableDelegationSetInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetReusableDelegationSetInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetReusableDelegationSetInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing information about the specified reusable delegation // set. type GetReusableDelegationSetOutput struct { @@ -3404,6 +4001,25 @@ func (s GetTrafficPolicyInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetTrafficPolicyInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetTrafficPolicyInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + if s.Version == nil { + invalidParams.Add(request.NewErrParamRequired("Version")) + } + if s.Version != nil && *s.Version < 1 { + invalidParams.Add(request.NewErrParamMinValue("Version", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // To retrieve a count of all your traffic policy instances, send a GET request // to the /Route 53 API version/trafficpolicyinstancecount resource. type GetTrafficPolicyInstanceCountInput struct { @@ -3461,6 +4077,19 @@ func (s GetTrafficPolicyInstanceInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *GetTrafficPolicyInstanceInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "GetTrafficPolicyInstanceInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the resource record sets that // Amazon Route 53 created based on a specified traffic policy. type GetTrafficPolicyInstanceOutput struct { @@ -3505,6 +4134,10 @@ type HealthCheck struct { // A unique string that identifies the request to create the health check. CallerReference *string `min:"1" type:"string" required:"true"` + // For CLOUDWATCH_METRIC health checks, a complex type that contains information + // about the CloudWatch alarm that you're associating with the health check. + CloudWatchAlarmConfiguration *CloudWatchAlarmConfiguration `type:"structure"` + // A complex type that contains the health check configuration. HealthCheckConfig *HealthCheckConfig `type:"structure" required:"true"` @@ -3531,6 +4164,10 @@ func (s HealthCheck) GoString() string { type HealthCheckConfig struct { _ struct{} `type:"structure"` + // A complex type that contains information to uniquely identify the CloudWatch + // alarm that you're associating with a Route 53 health check. + AlarmIdentifier *AlarmIdentifier `type:"structure"` + // For a specified parent health check, a list of HealthCheckId values for the // associated child health checks. ChildHealthChecks []*string `locationNameList:"ChildHealthCheck" type:"list"` @@ -3562,6 +4199,10 @@ type HealthCheckConfig struct { // IP Address of the instance being checked. IPAddress *string `type:"string"` + // The status of the health check when CloudWatch has insufficient data about + // the state of associated alarm. Valid values are Healthy, Unhealthy and LastKnownStatus. + InsufficientDataHealthStatus *string `type:"string" enum:"InsufficientDataHealthStatus"` + // A boolean value that indicates whether the status of health check should // be inverted. For example, if a health check is healthy but Inverted is True, // then Amazon Route 53 considers the health check to be unhealthy. @@ -3577,6 +4218,11 @@ type HealthCheckConfig struct { // For HTTPS and HTTPS_STR_MATCH this defaults to 443 if the port is not specified. Port *int64 `min:"1" type:"integer"` + // A list of HealthCheckRegion values that you want Amazon Route 53 to use to + // perform health checks for the specified endpoint. You must specify at least + // three regions. + Regions []*string `locationNameList:"Region" min:"1" type:"list"` + // The number of seconds between the time that Amazon Route 53 gets a response // from your endpoint and the time that it sends the next health-check request. // @@ -3595,7 +4241,7 @@ type HealthCheckConfig struct { SearchString *string `type:"string"` // The type of health check to be performed. Currently supported types are TCP, - // HTTP, HTTPS, HTTP_STR_MATCH, and HTTPS_STR_MATCH. + // HTTP, HTTPS, HTTP_STR_MATCH, HTTPS_STR_MATCH, CALCULATED and CLOUDWATCH_METRIC. Type *string `type:"string" required:"true" enum:"HealthCheckType"` } @@ -3609,15 +4255,49 @@ func (s HealthCheckConfig) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *HealthCheckConfig) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "HealthCheckConfig"} + if s.FailureThreshold != nil && *s.FailureThreshold < 1 { + invalidParams.Add(request.NewErrParamMinValue("FailureThreshold", 1)) + } + if s.Port != nil && *s.Port < 1 { + invalidParams.Add(request.NewErrParamMinValue("Port", 1)) + } + if s.Regions != nil && len(s.Regions) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Regions", 1)) + } + if s.RequestInterval != nil && *s.RequestInterval < 10 { + invalidParams.Add(request.NewErrParamMinValue("RequestInterval", 10)) + } + if s.Type == nil { + invalidParams.Add(request.NewErrParamRequired("Type")) + } + if s.AlarmIdentifier != nil { + if err := s.AlarmIdentifier.Validate(); err != nil { + invalidParams.AddNested("AlarmIdentifier", err.(request.ErrInvalidParams)) + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains the IP address of a Amazon Route 53 health checker // and the reason for the health check status. type HealthCheckObservation struct { _ struct{} `type:"structure"` - // The IP address of the Amazon Route 53 health checker that performed the health - // check. + // The IP address of the Amazon Route 53 health checker that performed this + // health check. IPAddress *string `type:"string"` + // The HealthCheckRegion of the Amazon Route 53 health checker that performed + // this health check. + Region *string `min:"1" type:"string" enum:"HealthCheckRegion"` + // A complex type that contains information about the health check status for // the current observation. StatusReport *StatusReport `type:"structure"` @@ -3725,6 +4405,25 @@ func (s ListChangeBatchesByHostedZoneInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ListChangeBatchesByHostedZoneInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ListChangeBatchesByHostedZoneInput"} + if s.EndDate == nil { + invalidParams.Add(request.NewErrParamRequired("EndDate")) + } + if s.HostedZoneId == nil { + invalidParams.Add(request.NewErrParamRequired("HostedZoneId")) + } + if s.StartDate == nil { + invalidParams.Add(request.NewErrParamRequired("StartDate")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // The input for a ListChangeBatchesByHostedZone request. type ListChangeBatchesByHostedZoneOutput struct { _ struct{} `deprecated:"true" type:"structure"` @@ -3794,6 +4493,34 @@ func (s ListChangeBatchesByRRSetInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ListChangeBatchesByRRSetInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ListChangeBatchesByRRSetInput"} + if s.EndDate == nil { + invalidParams.Add(request.NewErrParamRequired("EndDate")) + } + if s.HostedZoneId == nil { + invalidParams.Add(request.NewErrParamRequired("HostedZoneId")) + } + if s.Name == nil { + invalidParams.Add(request.NewErrParamRequired("Name")) + } + if s.SetIdentifier != nil && len(*s.SetIdentifier) < 1 { + invalidParams.Add(request.NewErrParamMinLen("SetIdentifier", 1)) + } + if s.StartDate == nil { + invalidParams.Add(request.NewErrParamRequired("StartDate")) + } + if s.Type == nil { + invalidParams.Add(request.NewErrParamRequired("Type")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // The input for a ListChangeBatchesByRRSet request. type ListChangeBatchesByRRSetOutput struct { _ struct{} `deprecated:"true" type:"structure"` @@ -3866,6 +4593,25 @@ func (s ListGeoLocationsInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ListGeoLocationsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ListGeoLocationsInput"} + if s.StartContinentCode != nil && len(*s.StartContinentCode) < 2 { + invalidParams.Add(request.NewErrParamMinLen("StartContinentCode", 2)) + } + if s.StartCountryCode != nil && len(*s.StartCountryCode) < 1 { + invalidParams.Add(request.NewErrParamMinLen("StartCountryCode", 1)) + } + if s.StartSubdivisionCode != nil && len(*s.StartSubdivisionCode) < 1 { + invalidParams.Add(request.NewErrParamMinLen("StartSubdivisionCode", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the geo locations that are // returned by the request and information about the response. type ListGeoLocationsOutput struct { @@ -4210,6 +4956,22 @@ func (s ListResourceRecordSetsInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ListResourceRecordSetsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ListResourceRecordSetsInput"} + if s.HostedZoneId == nil { + invalidParams.Add(request.NewErrParamRequired("HostedZoneId")) + } + if s.StartRecordIdentifier != nil && len(*s.StartRecordIdentifier) < 1 { + invalidParams.Add(request.NewErrParamMinLen("StartRecordIdentifier", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the resource record sets that // are returned by the request and information about the response. type ListResourceRecordSetsOutput struct { @@ -4361,6 +5123,22 @@ func (s ListTagsForResourceInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ListTagsForResourceInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ListTagsForResourceInput"} + if s.ResourceId == nil { + invalidParams.Add(request.NewErrParamRequired("ResourceId")) + } + if s.ResourceType == nil { + invalidParams.Add(request.NewErrParamRequired("ResourceType")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing tags for the specified resource. type ListTagsForResourceOutput struct { _ struct{} `type:"structure"` @@ -4406,6 +5184,25 @@ func (s ListTagsForResourcesInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ListTagsForResourcesInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ListTagsForResourcesInput"} + if s.ResourceIds == nil { + invalidParams.Add(request.NewErrParamRequired("ResourceIds")) + } + if s.ResourceIds != nil && len(s.ResourceIds) < 1 { + invalidParams.Add(request.NewErrParamMinLen("ResourceIds", 1)) + } + if s.ResourceType == nil { + invalidParams.Add(request.NewErrParamRequired("ResourceType")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing tags for the specified resources. type ListTagsForResourcesOutput struct { _ struct{} `type:"structure"` @@ -4546,6 +5343,19 @@ func (s ListTrafficPolicyInstancesByHostedZoneInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ListTrafficPolicyInstancesByHostedZoneInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ListTrafficPolicyInstancesByHostedZoneInput"} + if s.HostedZoneId == nil { + invalidParams.Add(request.NewErrParamRequired("HostedZoneId")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains the response information for the request. type ListTrafficPolicyInstancesByHostedZoneOutput struct { _ struct{} `type:"structure"` @@ -4657,6 +5467,25 @@ func (s ListTrafficPolicyInstancesByPolicyInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ListTrafficPolicyInstancesByPolicyInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ListTrafficPolicyInstancesByPolicyInput"} + if s.TrafficPolicyId == nil { + invalidParams.Add(request.NewErrParamRequired("TrafficPolicyId")) + } + if s.TrafficPolicyVersion == nil { + invalidParams.Add(request.NewErrParamRequired("TrafficPolicyVersion")) + } + if s.TrafficPolicyVersion != nil && *s.TrafficPolicyVersion < 1 { + invalidParams.Add(request.NewErrParamMinValue("TrafficPolicyVersion", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains the response information for the request. type ListTrafficPolicyInstancesByPolicyOutput struct { _ struct{} `type:"structure"` @@ -4850,6 +5679,19 @@ func (s ListTrafficPolicyVersionsInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ListTrafficPolicyVersionsInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ListTrafficPolicyVersionsInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains the response information for the request. type ListTrafficPolicyVersionsOutput struct { _ struct{} `type:"structure"` @@ -4915,6 +5757,19 @@ func (s ResourceRecord) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ResourceRecord) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ResourceRecord"} + if s.Value == nil { + invalidParams.Add(request.NewErrParamRequired("Value")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the current resource record // set. type ResourceRecordSet struct { @@ -5144,6 +5999,51 @@ func (s ResourceRecordSet) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *ResourceRecordSet) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ResourceRecordSet"} + if s.Name == nil { + invalidParams.Add(request.NewErrParamRequired("Name")) + } + if s.Region != nil && len(*s.Region) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Region", 1)) + } + if s.ResourceRecords != nil && len(s.ResourceRecords) < 1 { + invalidParams.Add(request.NewErrParamMinLen("ResourceRecords", 1)) + } + if s.SetIdentifier != nil && len(*s.SetIdentifier) < 1 { + invalidParams.Add(request.NewErrParamMinLen("SetIdentifier", 1)) + } + if s.Type == nil { + invalidParams.Add(request.NewErrParamRequired("Type")) + } + if s.AliasTarget != nil { + if err := s.AliasTarget.Validate(); err != nil { + invalidParams.AddNested("AliasTarget", err.(request.ErrInvalidParams)) + } + } + if s.GeoLocation != nil { + if err := s.GeoLocation.Validate(); err != nil { + invalidParams.AddNested("GeoLocation", err.(request.ErrInvalidParams)) + } + } + if s.ResourceRecords != nil { + for i, v := range s.ResourceRecords { + if v == nil { + continue + } + if err := v.Validate(); err != nil { + invalidParams.AddNested(fmt.Sprintf("%s[%v]", "ResourceRecords", i), err.(request.ErrInvalidParams)) + } + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing a resource and its associated tags. type ResourceTagSet struct { _ struct{} `type:"structure"` @@ -5305,6 +6205,10 @@ func (s TrafficPolicySummary) GoString() string { type UpdateHealthCheckInput struct { _ struct{} `locationName:"UpdateHealthCheckRequest" type:"structure" xmlURI:"https://route53.amazonaws.com/doc/2013-04-01/"` + // A complex type that contains information to uniquely identify the CloudWatch + // alarm that you're associating with a Route 53 health check. + AlarmIdentifier *AlarmIdentifier `type:"structure"` + // For a specified parent health check, a list of HealthCheckId values for the // associated child health checks. // @@ -5357,6 +6261,8 @@ type UpdateHealthCheckInput struct { // Specify this value only if you want to change it. IPAddress *string `type:"string"` + InsufficientDataHealthStatus *string `type:"string" enum:"InsufficientDataHealthStatus"` + // A boolean value that indicates whether the status of health check should // be inverted. For example, if a health check is healthy but Inverted is True, // then Amazon Route 53 considers the health check to be unhealthy. @@ -5370,6 +6276,15 @@ type UpdateHealthCheckInput struct { // Specify this value only if you want to change it. Port *int64 `min:"1" type:"integer"` + // A list of HealthCheckRegion values that specify the Amazon EC2 regions that + // you want Amazon Route 53 to use to perform health checks. You must specify + // at least three regions. + // + // When you remove a region from the list, Amazon Route 53 will briefly continue + // to check your endpoint from that region. Specify this value only if you want + // to change it. + Regions []*string `locationNameList:"Region" min:"1" type:"list"` + // The path that you want Amazon Route 53 to request when performing health // checks. The path can be any value for which your endpoint will return an // HTTP status code of 2xx or 3xx when the endpoint is healthy, for example @@ -5398,6 +6313,36 @@ func (s UpdateHealthCheckInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *UpdateHealthCheckInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "UpdateHealthCheckInput"} + if s.FailureThreshold != nil && *s.FailureThreshold < 1 { + invalidParams.Add(request.NewErrParamMinValue("FailureThreshold", 1)) + } + if s.HealthCheckId == nil { + invalidParams.Add(request.NewErrParamRequired("HealthCheckId")) + } + if s.HealthCheckVersion != nil && *s.HealthCheckVersion < 1 { + invalidParams.Add(request.NewErrParamMinValue("HealthCheckVersion", 1)) + } + if s.Port != nil && *s.Port < 1 { + invalidParams.Add(request.NewErrParamMinValue("Port", 1)) + } + if s.Regions != nil && len(s.Regions) < 1 { + invalidParams.Add(request.NewErrParamMinLen("Regions", 1)) + } + if s.AlarmIdentifier != nil { + if err := s.AlarmIdentifier.Validate(); err != nil { + invalidParams.AddNested("AlarmIdentifier", err.(request.ErrInvalidParams)) + } + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + type UpdateHealthCheckOutput struct { _ struct{} `type:"structure"` @@ -5437,6 +6382,19 @@ func (s UpdateHostedZoneCommentInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *UpdateHostedZoneCommentInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "UpdateHostedZoneCommentInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type containing information about the specified hosted zone after // the update. type UpdateHostedZoneCommentOutput struct { @@ -5482,6 +6440,28 @@ func (s UpdateTrafficPolicyCommentInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *UpdateTrafficPolicyCommentInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "UpdateTrafficPolicyCommentInput"} + if s.Comment == nil { + invalidParams.Add(request.NewErrParamRequired("Comment")) + } + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + if s.Version == nil { + invalidParams.Add(request.NewErrParamRequired("Version")) + } + if s.Version != nil && *s.Version < 1 { + invalidParams.Add(request.NewErrParamMinValue("Version", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains the response information for the traffic policy. type UpdateTrafficPolicyCommentOutput struct { _ struct{} `type:"structure"` @@ -5531,6 +6511,31 @@ func (s UpdateTrafficPolicyInstanceInput) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *UpdateTrafficPolicyInstanceInput) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "UpdateTrafficPolicyInstanceInput"} + if s.Id == nil { + invalidParams.Add(request.NewErrParamRequired("Id")) + } + if s.TTL == nil { + invalidParams.Add(request.NewErrParamRequired("TTL")) + } + if s.TrafficPolicyId == nil { + invalidParams.Add(request.NewErrParamRequired("TrafficPolicyId")) + } + if s.TrafficPolicyVersion == nil { + invalidParams.Add(request.NewErrParamRequired("TrafficPolicyVersion")) + } + if s.TrafficPolicyVersion != nil && *s.TrafficPolicyVersion < 1 { + invalidParams.Add(request.NewErrParamMinValue("TrafficPolicyVersion", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + // A complex type that contains information about the resource record sets that // Amazon Route 53 created based on a specified traffic policy. type UpdateTrafficPolicyInstanceOutput struct { @@ -5569,6 +6574,19 @@ func (s VPC) GoString() string { return s.String() } +// Validate inspects the fields of the type to determine if they are valid. +func (s *VPC) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "VPC"} + if s.VPCRegion != nil && len(*s.VPCRegion) < 1 { + invalidParams.Add(request.NewErrParamMinLen("VPCRegion", 1)) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + const ( // @enum ChangeAction ChangeActionCreate = "CREATE" @@ -5585,6 +6603,61 @@ const ( ChangeStatusInsync = "INSYNC" ) +const ( + // @enum CloudWatchRegion + CloudWatchRegionUsEast1 = "us-east-1" + // @enum CloudWatchRegion + CloudWatchRegionUsWest1 = "us-west-1" + // @enum CloudWatchRegion + CloudWatchRegionUsWest2 = "us-west-2" + // @enum CloudWatchRegion + CloudWatchRegionEuCentral1 = "eu-central-1" + // @enum CloudWatchRegion + CloudWatchRegionEuWest1 = "eu-west-1" + // @enum CloudWatchRegion + CloudWatchRegionApSoutheast1 = "ap-southeast-1" + // @enum CloudWatchRegion + CloudWatchRegionApSoutheast2 = "ap-southeast-2" + // @enum CloudWatchRegion + CloudWatchRegionApNortheast1 = "ap-northeast-1" + // @enum CloudWatchRegion + CloudWatchRegionApNortheast2 = "ap-northeast-2" + // @enum CloudWatchRegion + CloudWatchRegionSaEast1 = "sa-east-1" +) + +const ( + // @enum ComparisonOperator + ComparisonOperatorGreaterThanOrEqualToThreshold = "GreaterThanOrEqualToThreshold" + // @enum ComparisonOperator + ComparisonOperatorGreaterThanThreshold = "GreaterThanThreshold" + // @enum ComparisonOperator + ComparisonOperatorLessThanThreshold = "LessThanThreshold" + // @enum ComparisonOperator + ComparisonOperatorLessThanOrEqualToThreshold = "LessThanOrEqualToThreshold" +) + +// An Amazon EC2 region that you want Amazon Route 53 to use to perform health +// checks. +const ( + // @enum HealthCheckRegion + HealthCheckRegionUsEast1 = "us-east-1" + // @enum HealthCheckRegion + HealthCheckRegionUsWest1 = "us-west-1" + // @enum HealthCheckRegion + HealthCheckRegionUsWest2 = "us-west-2" + // @enum HealthCheckRegion + HealthCheckRegionEuWest1 = "eu-west-1" + // @enum HealthCheckRegion + HealthCheckRegionApSoutheast1 = "ap-southeast-1" + // @enum HealthCheckRegion + HealthCheckRegionApSoutheast2 = "ap-southeast-2" + // @enum HealthCheckRegion + HealthCheckRegionApNortheast1 = "ap-northeast-1" + // @enum HealthCheckRegion + HealthCheckRegionSaEast1 = "sa-east-1" +) + const ( // @enum HealthCheckType HealthCheckTypeHttp = "HTTP" @@ -5598,6 +6671,17 @@ const ( HealthCheckTypeTcp = "TCP" // @enum HealthCheckType HealthCheckTypeCalculated = "CALCULATED" + // @enum HealthCheckType + HealthCheckTypeCloudwatchMetric = "CLOUDWATCH_METRIC" +) + +const ( + // @enum InsufficientDataHealthStatus + InsufficientDataHealthStatusHealthy = "Healthy" + // @enum InsufficientDataHealthStatus + InsufficientDataHealthStatusUnhealthy = "Unhealthy" + // @enum InsufficientDataHealthStatus + InsufficientDataHealthStatusLastKnownStatus = "LastKnownStatus" ) const ( @@ -5655,6 +6739,19 @@ const ( ResourceRecordSetRegionCnNorth1 = "cn-north-1" ) +const ( + // @enum Statistic + StatisticAverage = "Average" + // @enum Statistic + StatisticSum = "Sum" + // @enum Statistic + StatisticSampleCount = "SampleCount" + // @enum Statistic + StatisticMaximum = "Maximum" + // @enum Statistic + StatisticMinimum = "Minimum" +) + const ( // @enum TagResourceType TagResourceTypeHealthcheck = "healthcheck" diff --git a/Godeps/_workspace/src/github.com/miekg/dns/README.md b/Godeps/_workspace/src/github.com/miekg/dns/README.md index 011d085..30152cf 100644 --- a/Godeps/_workspace/src/github.com/miekg/dns/README.md +++ b/Godeps/_workspace/src/github.com/miekg/dns/README.md @@ -12,7 +12,7 @@ can build servers and resolvers with it. We try to keep the "master" branch as sane as possible and at the bleeding edge of standards, avoiding breaking changes wherever reasonable. We support the last -two versions of Go, currently: 1.4 and 1.5. +two versions of Go, currently: 1.5 and 1.6. # Goals @@ -48,6 +48,8 @@ A not-so-up-to-date-list-that-may-be-actually-current: * https://github.com/miekg/unbound * https://github.com/miekg/exdns * https://dnslookup.org +* https://github.com/looterz/grimd +* https://github.com/phamhongviet/serf-dns Send pull request if you want to be listed here. @@ -61,7 +63,7 @@ Send pull request if you want to be listed here. * Server side programming (mimicking the net/http package); * Client side programming; * DNSSEC: signing, validating and key generation for DSA, RSA and ECDSA; -* EDNS0, NSID; +* EDNS0, NSID, Cookies; * AXFR/IXFR; * TSIG, SIG(0); * DNS over TLS: optional encrypted connection between client and server; @@ -140,6 +142,7 @@ Example programs can be found in the `github.com/miekg/exdns` repository. * 7553 - URI record * xxxx - EDNS0 DNS Update Lease (draft) * yyyy - DNS over TLS: Initiation and Performance Considerations (draft) +* xxxx - Domain Name System (DNS) Cookies (draft-ietf-dnsop-cookies) ## Loosely based upon diff --git a/Godeps/_workspace/src/github.com/miekg/dns/client.go b/Godeps/_workspace/src/github.com/miekg/dns/client.go index 7a05eb9..c1a4a43 100644 --- a/Godeps/_workspace/src/github.com/miekg/dns/client.go +++ b/Godeps/_workspace/src/github.com/miekg/dns/client.go @@ -28,9 +28,10 @@ type Client struct { Net string // if "tcp" or "tcp-tls" (DNS over TLS) a TCP query will be initiated, otherwise an UDP one (default is "" for UDP) UDPSize uint16 // minimum receive buffer for UDP messages TLSConfig *tls.Config // TLS connection configuration - DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds - ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds + Timeout time.Duration // a cumulative timeout for dial, write and read, defaults to 0 (disabled) - overrides DialTimeout, ReadTimeout and WriteTimeout when non-zero + DialTimeout time.Duration // net.DialTimeout, defaults to 2 seconds - overridden by Timeout when that value is non-zero + ReadTimeout time.Duration // net.Conn.SetReadTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero + WriteTimeout time.Duration // net.Conn.SetWriteTimeout value for connections, defaults to 2 seconds - overridden by Timeout when that value is non-zero TsigSecret map[string]string // secret(s) for Tsig map[], zonename must be fully qualified SingleInflight bool // if true suppress multiple outstanding queries for the same Qname, Qtype and Qclass group singleflight @@ -129,6 +130,9 @@ func (c *Client) Exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro } func (c *Client) dialTimeout() time.Duration { + if c.Timeout != 0 { + return c.Timeout + } if c.DialTimeout != 0 { return c.DialTimeout } @@ -170,6 +174,11 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro } } + var deadline time.Time + if c.Timeout != 0 { + deadline = time.Now().Add(c.Timeout) + } + if tls { co, err = DialTimeoutWithTLS(network, a, c.TLSConfig, c.dialTimeout()) } else { @@ -192,12 +201,12 @@ func (c *Client) exchange(m *Msg, a string) (r *Msg, rtt time.Duration, err erro } co.TsigSecret = c.TsigSecret - co.SetWriteDeadline(time.Now().Add(c.writeTimeout())) + co.SetWriteDeadline(deadlineOrTimeout(deadline, c.writeTimeout())) if err = co.WriteMsg(m); err != nil { return nil, 0, err } - co.SetReadDeadline(time.Now().Add(c.readTimeout())) + co.SetReadDeadline(deadlineOrTimeout(deadline, c.readTimeout())) r, err = co.ReadMsg() if err == nil && r.Id != m.Id { err = ErrId @@ -434,3 +443,10 @@ func DialTimeoutWithTLS(network, address string, tlsConfig *tls.Config, timeout } return conn, nil } + +func deadlineOrTimeout(deadline time.Time, timeout time.Duration) time.Time { + if deadline.IsZero() { + return time.Now().Add(timeout) + } + return deadline +} diff --git a/Godeps/_workspace/src/github.com/miekg/dns/defaults.go b/Godeps/_workspace/src/github.com/miekg/dns/defaults.go index 63165b4..cf45616 100644 --- a/Godeps/_workspace/src/github.com/miekg/dns/defaults.go +++ b/Godeps/_workspace/src/github.com/miekg/dns/defaults.go @@ -142,9 +142,13 @@ func (dns *Msg) IsTsig() *TSIG { // record in the additional section will do. It returns the OPT record // found or nil. func (dns *Msg) IsEdns0() *OPT { - for _, r := range dns.Extra { - if r.Header().Rrtype == TypeOPT { - return r.(*OPT) + // EDNS0 is at the end of the additional section, start there. + // We might want to change this to *only* look at the last two + // records. So we see TSIG and/or OPT - this a slightly bigger + // change though. + for i := len(dns.Extra) - 1; i >= 0; i-- { + if dns.Extra[i].Header().Rrtype == TypeOPT { + return dns.Extra[i].(*OPT) } } return nil @@ -163,8 +167,8 @@ func IsDomainName(s string) (labels int, ok bool) { return labels, err == nil } -// IsSubDomain checks if child is indeed a child of the parent. Both child and -// parent are *not* downcased before doing the comparison. +// IsSubDomain checks if child is indeed a child of the parent. If child and parent +// are the same domain true is returned as well. func IsSubDomain(parent, child string) bool { // Entire child is contained in parent return CompareDomainName(parent, child) == CountLabel(parent) diff --git a/Godeps/_workspace/src/github.com/miekg/dns/edns.go b/Godeps/_workspace/src/github.com/miekg/dns/edns.go index 0c47f6e..c1aebc1 100644 --- a/Godeps/_workspace/src/github.com/miekg/dns/edns.go +++ b/Godeps/_workspace/src/github.com/miekg/dns/edns.go @@ -17,6 +17,7 @@ const ( EDNS0N3U = 0x7 // NSEC3 Hash Understood EDNS0SUBNET = 0x8 // client-subnet (RFC6891) EDNS0EXPIRE = 0x9 // EDNS0 expire + EDNS0COOKIE = 0xa // EDNS0 Cookie EDNS0SUBNETDRAFT = 0x50fa // Don't use! Use EDNS0SUBNET EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (RFC6891) EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (RFC6891) @@ -56,6 +57,8 @@ func (rr *OPT) String() string { if o.(*EDNS0_SUBNET).DraftOption { s += " (draft)" } + case *EDNS0_COOKIE: + s += "\n; COOKIE: " + o.String() case *EDNS0_UL: s += "\n; UPDATE LEASE: " + o.String() case *EDNS0_LLQ: @@ -96,13 +99,16 @@ func (rr *OPT) SetVersion(v uint8) { } // ExtendedRcode returns the EDNS extended RCODE field (the upper 8 bits of the TTL). -func (rr *OPT) ExtendedRcode() uint8 { - return uint8((rr.Hdr.Ttl & 0xFF000000) >> 24) +func (rr *OPT) ExtendedRcode() int { + return int((rr.Hdr.Ttl&0xFF000000)>>24) + 15 } // SetExtendedRcode sets the EDNS extended RCODE field. func (rr *OPT) SetExtendedRcode(v uint8) { - rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v) << 24) + if v < RcodeBadVers { // Smaller than 16.. Use the 4 bits you have! + return + } + rr.Hdr.Ttl = rr.Hdr.Ttl&0x00FFFFFF | (uint32(v-15) << 24) } // UDPSize returns the UDP buffer size. @@ -283,6 +289,33 @@ func (e *EDNS0_SUBNET) String() (s string) { return } +// The Cookie EDNS0 option +// +// o := new(dns.OPT) +// o.Hdr.Name = "." +// o.Hdr.Rrtype = dns.TypeOPT +// e := new(dns.EDNS0_COOKIE) +// e.Code = dns.EDNS0COOKIE +// e.Cookie = "24a5ac.." +// o.Option = append(o.Option, e) +// +type EDNS0_COOKIE struct { + Code uint16 // Always EDNS0COOKIE + Cookie string // Hex-encoded cookie data +} + +func (e *EDNS0_COOKIE) pack() ([]byte, error) { + h, err := hex.DecodeString(e.Cookie) + if err != nil { + return nil, err + } + return h, nil +} + +func (e *EDNS0_COOKIE) Option() uint16 { return EDNS0COOKIE } +func (e *EDNS0_COOKIE) unpack(b []byte) error { e.Cookie = hex.EncodeToString(b); return nil } +func (e *EDNS0_COOKIE) String() string { return e.Cookie } + // The EDNS0_UL (Update Lease) (draft RFC) option is used to tell the server to set // an expiration on an update RR. This is helpful for clients that cannot clean // up after themselves. This is a draft RFC and more information can be found at diff --git a/Godeps/_workspace/src/github.com/miekg/dns/labels.go b/Godeps/_workspace/src/github.com/miekg/dns/labels.go index cb549fc..fca5c7d 100644 --- a/Godeps/_workspace/src/github.com/miekg/dns/labels.go +++ b/Godeps/_workspace/src/github.com/miekg/dns/labels.go @@ -107,7 +107,7 @@ func CountLabel(s string) (labels int) { // Split splits a name s into its label indexes. // www.miek.nl. returns []int{0, 4, 9}, www.miek.nl also returns []int{0, 4, 9}. -// The root name (.) returns nil. Also see SplitDomainName. +// The root name (.) returns nil. Also see SplitDomainName. // s must be a syntactically valid domain name. func Split(s string) []int { if s == "." { diff --git a/Godeps/_workspace/src/github.com/miekg/dns/msg.go b/Godeps/_workspace/src/github.com/miekg/dns/msg.go index 692ad64..41b6e0d 100644 --- a/Godeps/_workspace/src/github.com/miekg/dns/msg.go +++ b/Godeps/_workspace/src/github.com/miekg/dns/msg.go @@ -596,7 +596,7 @@ func packStructValue(val reflect.Value, msg []byte, off int, compression map[str for j := 0; j < val.Field(i).Len(); j++ { element := val.Field(i).Index(j).Interface() b, e := element.(EDNS0).pack() - if e != nil { + if e != nil || off+3 > lenmsg { return lenmsg, &Error{err: "overflow packing opt"} } // Option code @@ -953,6 +953,13 @@ func unpackStructValue(val reflect.Value, msg []byte, off int) (off1 int, err er if code == EDNS0SUBNETDRAFT { e.DraftOption = true } + case EDNS0COOKIE: + e := new(EDNS0_COOKIE) + if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil { + return lenmsg, err + } + edns = append(edns, e) + off = off1 + int(optlen) case EDNS0UL: e := new(EDNS0_UL) if err := e.unpack(msg[off1 : off1+int(optlen)]); err != nil { @@ -1896,6 +1903,11 @@ func Copy(r RR) RR { return r1 } +// Len returns the length (in octets) of the uncompressed RR in wire format. +func Len(r RR) int { + return r.len() +} + // Copy returns a new *Msg which is a deep-copy of dns. func (dns *Msg) Copy() *Msg { return dns.CopyTo(new(Msg)) diff --git a/Godeps/_workspace/src/github.com/miekg/dns/types_generate.go b/Godeps/_workspace/src/github.com/miekg/dns/types_generate.go index b8d1cd2..63bfda0 100644 --- a/Godeps/_workspace/src/github.com/miekg/dns/types_generate.go +++ b/Godeps/_workspace/src/github.com/miekg/dns/types_generate.go @@ -229,7 +229,10 @@ func main() { if sl, ok := st.Field(i).Type().(*types.Slice); ok { t := sl.Underlying().String() t = strings.TrimPrefix(t, "[]") - t = strings.TrimPrefix(t, "github.com/miekg/dns.") + if strings.Contains(t, ".") { + splits := strings.Split(t, ".") + t = splits[len(splits)-1] + } fmt.Fprintf(b, "%s := make([]%s, len(rr.%s)); copy(%s, rr.%s)\n", f, t, f, f, f) fields = append(fields, f) diff --git a/Godeps/_workspace/src/github.com/miekg/dns/udp.go b/Godeps/_workspace/src/github.com/miekg/dns/udp.go index fc86563..c79c6c8 100644 --- a/Godeps/_workspace/src/github.com/miekg/dns/udp.go +++ b/Godeps/_workspace/src/github.com/miekg/dns/udp.go @@ -1,4 +1,4 @@ -// +build !windows +// +build !windows,!plan9 package dns diff --git a/Godeps/_workspace/src/github.com/miekg/dns/udp_other.go b/Godeps/_workspace/src/github.com/miekg/dns/udp_other.go index c38dd3e..d407324 100644 --- a/Godeps/_workspace/src/github.com/miekg/dns/udp_other.go +++ b/Godeps/_workspace/src/github.com/miekg/dns/udp_other.go @@ -1,4 +1,4 @@ -// +build !linux +// +build !linux,!plan9 package dns diff --git a/Godeps/_workspace/src/github.com/miekg/dns/udp_plan9.go b/Godeps/_workspace/src/github.com/miekg/dns/udp_plan9.go new file mode 100644 index 0000000..b794dee --- /dev/null +++ b/Godeps/_workspace/src/github.com/miekg/dns/udp_plan9.go @@ -0,0 +1,34 @@ +package dns + +import ( + "net" +) + +func setUDPSocketOptions(conn *net.UDPConn) error { return nil } + +// SessionUDP holds the remote address and the associated +// out-of-band data. +type SessionUDP struct { + raddr *net.UDPAddr + context []byte +} + +// RemoteAddr returns the remote network address. +func (s *SessionUDP) RemoteAddr() net.Addr { return s.raddr } + +// ReadFromSessionUDP acts just like net.UDPConn.ReadFrom(), but returns a session object instead of a +// net.UDPAddr. +func ReadFromSessionUDP(conn *net.UDPConn, b []byte) (int, *SessionUDP, error) { + oob := make([]byte, 40) + n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob) + if err != nil { + return n, nil, err + } + return n, &SessionUDP{raddr, oob[:oobn]}, err +} + +// WriteToSessionUDP acts just like net.UDPConn.WritetTo(), but uses a *SessionUDP instead of a net.Addr. +func WriteToSessionUDP(conn *net.UDPConn, b []byte, session *SessionUDP) (int, error) { + n, _, err := conn.WriteMsgUDP(b, session.context, session.raddr) + return n, err +} diff --git a/Godeps/_workspace/src/github.com/miekg/dns/xfr.go b/Godeps/_workspace/src/github.com/miekg/dns/xfr.go index 7d3a67b..7346def 100644 --- a/Godeps/_workspace/src/github.com/miekg/dns/xfr.go +++ b/Godeps/_workspace/src/github.com/miekg/dns/xfr.go @@ -162,8 +162,8 @@ func (t *Transfer) inIxfr(id uint16, c chan *Envelope) { // // ch := make(chan *dns.Envelope) // tr := new(dns.Transfer) -// tr.Out(w, r, ch) -// c <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}} +// go tr.Out(w, r, ch) +// ch <- &dns.Envelope{RR: []dns.RR{soa, rr1, rr2, rr3, soa}} // close(ch) // w.Hijack() // // w.Close() // Client closes connection diff --git a/Godeps/_workspace/src/github.com/xenolf/lego/acme/dns_challenge.go b/Godeps/_workspace/src/github.com/xenolf/lego/acme/dns_challenge.go index b77e916..2f45e2a 100644 --- a/Godeps/_workspace/src/github.com/xenolf/lego/acme/dns_challenge.go +++ b/Godeps/_workspace/src/github.com/xenolf/lego/acme/dns_challenge.go @@ -26,6 +26,9 @@ var RecursiveNameservers = []string{ "google-public-dns-b.google.com:53", } +// DNSTimeout is used to override the default DNS timeout of 10 seconds. +var DNSTimeout = 10 * time.Second + // DNS01Record returns a DNS record which will fulfill the `dns-01` challenge func DNS01Record(domain, keyAuth string) (fqdn string, value string, ttl int) { keyAuthShaBytes := sha256.Sum256([]byte(keyAuth)) @@ -161,10 +164,11 @@ func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) ( // Will retry the request based on the number of servers (n+1) for i := 1; i <= len(nameservers)+1; i++ { ns := nameservers[i%len(nameservers)] - in, err = dns.Exchange(m, ns) + udp := &dns.Client{Net: "udp", Timeout: DNSTimeout} + in, _, err = udp.Exchange(m, ns) if err == dns.ErrTruncated { - tcp := &dns.Client{Net: "tcp"} + tcp := &dns.Client{Net: "tcp", Timeout: DNSTimeout} // If the TCP request suceeds, the err will reset to nil in, _, err = tcp.Exchange(m, ns) } diff --git a/Godeps/_workspace/src/github.com/xenolf/lego/acme/http.go b/Godeps/_workspace/src/github.com/xenolf/lego/acme/http.go index 410aead..3b5a37c 100644 --- a/Godeps/_workspace/src/github.com/xenolf/lego/acme/http.go +++ b/Godeps/_workspace/src/github.com/xenolf/lego/acme/http.go @@ -14,8 +14,11 @@ import ( // UserAgent (if non-empty) will be tacked onto the User-Agent string in requests. var UserAgent string +// HTTPTimeout is used to override the default HTTP timeout of 10 seconds. +var HTTPTimeout = 10 * time.Second + // defaultClient is an HTTP client with a reasonable timeout value. -var defaultClient = http.Client{Timeout: 10 * time.Second} +var defaultClient = http.Client{Timeout: HTTPTimeout} const ( // defaultGoUserAgent is the Go HTTP package user agent string. Too diff --git a/Godeps/_workspace/src/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go b/Godeps/_workspace/src/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go index e566224..3f869c1 100644 --- a/Godeps/_workspace/src/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go +++ b/Godeps/_workspace/src/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go @@ -156,8 +156,9 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) { func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) { // APIError contains error details for failed requests type APIError struct { - Code int `json:"code,omitempty"` - Message string `json:"message,omitempty"` + Code int `json:"code,omitempty"` + Message string `json:"message,omitempty"` + ErrorChain []APIError `json:"error_chain,omitempty"` } // APIResponse represents a response from CloudFlare API @@ -179,7 +180,7 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM client := http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { - return nil, fmt.Errorf("Error querying API -> %v", err) + return nil, fmt.Errorf("Error querying Cloudflare API -> %v", err) } defer resp.Body.Close() @@ -192,9 +193,16 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM if !r.Success { if len(r.Errors) > 0 { - return nil, fmt.Errorf("API error -> %d: %s", r.Errors[0].Code, r.Errors[0].Message) + errStr := "" + for _, apiErr := range r.Errors { + errStr += fmt.Sprintf("\t Error: %d: %s", apiErr.Code, apiErr.Message) + for _, chainErr := range apiErr.ErrorChain { + errStr += fmt.Sprintf("<- %d: %s", chainErr.Code, chainErr.Message) + } + } + return nil, fmt.Errorf("Cloudflare API Error \n%s", errStr) } - return nil, fmt.Errorf("API error") + return nil, fmt.Errorf("Cloudflare API error") } return r.Result, nil diff --git a/Makefile b/Makefile index 27db216..75dd2bc 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ help: @echo "make clean - Duh!" @echo "make release - tag with version and trigger CI release build" @echo "make image - build Docker image" - @echo "make dockerhub - build and push image to Docker Hub" + @echo "make dockerhub - build and push dev image to Docker Hub" @echo "make version - show app version" build: build-dir @@ -48,8 +48,8 @@ clean: rm -fr ./dist dockerhub: image - @echo "Pushing $(DOCKER_IMAGE):$(VERSION)" - docker push $(DOCKER_IMAGE):$(VERSION) + @echo "Pushing $(DOCKER_IMAGE):dev-$(SHA)" + docker push $(DOCKER_IMAGE):dev-$(SHA) image: docker build -t $(DOCKER_IMAGE):dev-$(SHA) -f Dockerfile.dev . diff --git a/circle.yml b/circle.yml index 0a004b4..3228545 100644 --- a/circle.yml +++ b/circle.yml @@ -9,13 +9,6 @@ general: - "dist/*.tar.gz" - "dist/*.sha512" -checkout: - post: - - git config user.name "Circle CI Build Bot" - - git config user.email "no-reply@circleci.com" - - git remote -v - - git fetch --tags - dependencies: pre: - go version @@ -25,6 +18,12 @@ dependencies: - make deps override: - echo "deps are vendored don't run go build" + post: + - git config user.name "Circle CI Build Bot" + - git config user.email "no-reply@circleci.com" + - git remote -v + - git fetch --tags + - git describe --abbrev=0 --tags test: pre: @@ -41,7 +40,7 @@ deployment: commands: - make ci-release - curl --data "docker_tag=latest" -X POST ${DOCKER_HUB_URI}/${DOCKER_HUB_TOKEN}/ - - sleep 30 + - sleep 60 - curl --data "source_type=Tag&source_name=${CIRCLE_TAG}" -X POST ${DOCKER_HUB_URI}/${DOCKER_HUB_TOKEN}/ - echo `git name-rev --tags --name-only $(git rev-parse HEAD)` - echo tag ${CIRCLE_TAG} diff --git a/context.go b/context.go index 1dcc49d..c9ff048 100644 --- a/context.go +++ b/context.go @@ -15,36 +15,35 @@ const ( CERT_DESCRIPTION = "Created by Let's Encrypt Certificate Manager" ISSUER_PRODUCTION = "Let's Encrypt" ISSUER_STAGING = "fake CA" - PREFIX_PROD = "[LE] " - PREFIX_STAGING = "[LE-TESTING] " ) type Context struct { Acme *letsencrypt.Client Rancher *rancher.Client - Domains []string - RenewalTime int + CertificateName string + Domains []string + RenewalTime int - ExpiryDate time.Time - RancherCertId string - RancherCertName string + ExpiryDate time.Time + RancherCertId string Debug bool } // InitContext initializes the application context from environmental variables func (c *Context) InitContext() { + var err error + c.Debug = debug cattleUrl := getEnvOption("CATTLE_URL", true) cattleApiKey := getEnvOption("CATTLE_ACCESS_KEY", true) cattleSecretKey := getEnvOption("CATTLE_SECRET_KEY", true) - debugParam := getEnvOption("DEBUG", false) eulaParam := getEnvOption("EULA", false) apiVerParam := getEnvOption("API_VERSION", true) emailParam := getEnvOption("EMAIL", true) domainParam := getEnvOption("DOMAINS", true) keyTypeParam := getEnvOption("PUBLIC_KEY_TYPE", true) - certNameParam := getEnvOption("CERT_NAME", false) + certNameParam := getEnvOption("CERT_NAME", true) timeParam := getEnvOption("RENEWAL_TIME", true) providerParam := getEnvOption("PROVIDER", true) @@ -52,38 +51,20 @@ func (c *Context) InitContext() { logrus.Fatalf("Terms of service were not accepted") } - var err error - c.Domains = listToSlice(domainParam) if len(c.Domains) == 0 { logrus.Fatalf("Invalid value for DOMAINS: %s", domainParam) } + c.CertificateName = certNameParam c.RenewalTime, err = strconv.Atoi(timeParam) if err != nil || c.RenewalTime < 0 || c.RenewalTime > 23 { logrus.Fatalf("Invalid value for RENEWAL_TIME: %s", timeParam) } - var certAutoName string - - switch apiVerParam { - case "Production": - certAutoName = PREFIX_PROD + c.Domains[0] - case "Sandbox": - certAutoName = PREFIX_STAGING + c.Domains[0] - default: - logrus.Fatalf("Invalid value for API_VERSION: %s", apiVerParam) - } - apiVersion := letsencrypt.ApiVersion(apiVerParam) keyType := letsencrypt.KeyType(keyTypeParam) - if len(certNameParam) != 0 { - c.RancherCertName = certNameParam - } else { - c.RancherCertName = certAutoName - } - c.Rancher, err = rancher.NewClient(cattleUrl, cattleApiKey, cattleSecretKey) if err != nil { logrus.Fatalf("Could not connect to Rancher API: %v", err) @@ -91,18 +72,18 @@ func (c *Context) InitContext() { providerOpts := letsencrypt.ProviderOpts{ Provider: letsencrypt.DnsProvider(providerParam), - CloudflareEmail: os.Getenv("CLOUDFLARE_EMAIL"), - CloudflareKey: os.Getenv("CLOUDFLARE_KEY"), - DoAccessToken: os.Getenv("DO_ACCESS_TOKEN"), - AwsAccessKey: os.Getenv("AWS_ACCESS_KEY"), - AwsSecretKey: os.Getenv("AWS_SECRET_KEY"), - DNSimpleEmail: os.Getenv("DNSIMPLE_EMAIL"), - DNSimpleKey: os.Getenv("DNSIMPLE_KEY"), - DynCustomerName: os.Getenv("DYN_CUSTOMER_NAME"), - DynUserName: os.Getenv("DYN_USER_NAME"), - DynPassword: os.Getenv("DYN_PASSWORD"), - NamecheapApiUser: os.Getenv("NAMECHEAP_API_USER"), - NamecheapApiKey: os.Getenv("NAMECHEAP_API_KEY"), + CloudflareEmail: getEnvOption("CLOUDFLARE_EMAIL", false), + CloudflareKey: getEnvOption("CLOUDFLARE_KEY", false), + DoAccessToken: getEnvOption("DO_ACCESS_TOKEN", false), + AwsAccessKey: getEnvOption("AWS_ACCESS_KEY", false), + AwsSecretKey: getEnvOption("AWS_SECRET_KEY", false), + DNSimpleEmail: getEnvOption("DNSIMPLE_EMAIL", false), + DNSimpleKey: getEnvOption("DNSIMPLE_KEY", false), + DynCustomerName: getEnvOption("DYN_CUSTOMER_NAME", false), + DynUserName: getEnvOption("DYN_USER_NAME", false), + DynPassword: getEnvOption("DYN_PASSWORD", false), + NamecheapApiUser: getEnvOption("NAMECHEAP_API_USER", false), + NamecheapApiKey: getEnvOption("NAMECHEAP_API_KEY", false), } c.Acme, err = letsencrypt.NewClient(emailParam, keyType, apiVersion, providerOpts) @@ -110,10 +91,9 @@ func (c *Context) InitContext() { logrus.Fatalf("LetsEncrypt client: %v", err) } - // Enable debug/test mode - if strings.EqualFold(debugParam, "true") { + // Enable debug mode + if c.Debug { logrus.SetLevel(logrus.DebugLevel) - c.Debug = true c.Acme.EnableDebug() } } @@ -123,10 +103,10 @@ func getEnvOption(name string, required bool) string { if required && len(val) == 0 { logrus.Fatalf("Required environment variable not set: %s", name) } - return val + return strings.TrimSpace(val) } func listToSlice(str string) []string { - str = strings.Join(strings.Fields(str), "") + str = strings.ToLower(strings.Join(strings.Fields(str), "")) return strings.Split(str, ",") } diff --git a/letsencrypt/account.go b/letsencrypt/account.go index a8646bc..45fc6ff 100644 --- a/letsencrypt/account.go +++ b/letsencrypt/account.go @@ -90,7 +90,7 @@ func (a *Account) GetRegistration() *lego.RegistrationResource { } func accountPath(email string, apiVer ApiVersion) string { - path := path.Join(ConfigDir, strings.ToLower(string(apiVer)), "accounts", email) + path := path.Join(StorageDir, strings.ToLower(string(apiVer)), "accounts", email) maybeCreatePath(path) return path } diff --git a/letsencrypt/client.go b/letsencrypt/client.go index c82aba2..ef60b72 100644 --- a/letsencrypt/client.go +++ b/letsencrypt/client.go @@ -7,6 +7,7 @@ import ( "log" "os" "path" + "regexp" "strings" "time" @@ -15,7 +16,7 @@ import ( ) const ( - ConfigDir = "/etc/letsencrypt" + StorageDir = "/etc/letsencrypt" ProductionApiUri = "https://acme-v01.api.letsencrypt.org/directory" StagingApiUri = "https://acme-staging.api.letsencrypt.org/directory" ) @@ -46,8 +47,9 @@ const ( // Domain string type AcmeCertificate struct { lego.CertificateResource + DnsNames string `json:"dnsNames"` ExpiryDate time.Time `json:"expiryDate"` - SerialNumber string `json:"serialnumber"` + SerialNumber string `json:"serialNumber"` } // Client represents a Lets Encrypt client @@ -81,7 +83,7 @@ func NewClient(email string, kt KeyType, apiVer ApiVersion, provider ProviderOpt case Sandbox: serverUri = StagingApiUri default: - return nil, fmt.Errorf("Invalid LE API version: %s", string(apiVer)) + return nil, fmt.Errorf("Invalid API version: %s", string(apiVer)) } acc, err := NewAccount(email, apiVer, keyType) @@ -143,82 +145,80 @@ func (c *Client) EnableDebug() { } // Issue obtains a new SAN certificate from the Lets Encrypt CA -func (c *Client) Issue(domains []string) (*AcmeCertificate, map[string]error) { - cert, failures := c.client.ObtainCertificate(domains, true, nil) +func (c *Client) Issue(certName string, domains []string) (*AcmeCertificate, map[string]error) { + certRes, failures := c.client.ObtainCertificate(domains, true, nil) if len(failures) > 0 { return nil, failures } - expirydate, _ := lego.GetPEMCertExpiration(cert.Certificate) - serialnum, _ := getPEMCertSerialNo(cert.Certificate) - - acmeCert := AcmeCertificate{ - CertificateResource: cert, - ExpiryDate: expirydate, - SerialNumber: serialnum, + dnsNames := dnsNamesIdentifier(domains) + acmeCert, err := c.saveCertificate(certName, dnsNames, certRes) + if err != nil { + logrus.Fatalf("Error saving certificate '%s': %v", certName, err) } - c.saveCertificate(acmeCert) - return &acmeCert, nil + return acmeCert, nil } -// Renew obtains a renewed SAN certificate from the Lets Encrypt CA -func (c *Client) Renew(domains []string) (*AcmeCertificate, error) { - domain := domains[0] - currentCert, err := c.loadCertificate(domain) +// Renew renewes the given stored certificate +func (c *Client) Renew(certName string) (*AcmeCertificate, error) { + acmeCert, err := c.loadCertificateByName(certName) if err != nil { - logrus.Fatalf(err.Error()) + return nil, fmt.Errorf("Error loading certificate '%s': %v", certName, err) } - certRes := currentCert.CertificateResource - newCert, err := c.client.RenewCertificate(certRes, true) + certRes := acmeCert.CertificateResource + newCertRes, err := c.client.RenewCertificate(certRes, true) if err != nil { return nil, err } - expirydate, _ := lego.GetPEMCertExpiration(newCert.Certificate) - serialnum, _ := getPEMCertSerialNo(newCert.Certificate) - - acmeCert := AcmeCertificate{ - CertificateResource: newCert, - ExpiryDate: expirydate, - SerialNumber: serialnum, + newAcmeCert, err := c.saveCertificate(certName, acmeCert.DnsNames, newCertRes) + if err != nil { + logrus.Fatalf("Error saving certificate '%s': %v", certName, err) } - c.saveCertificate(acmeCert) - return &acmeCert, nil + return newAcmeCert, nil } -// GetStoredCertificate returns a locally stored certificate for the given domains -func (c *Client) GetStoredCertificate(domains []string) (bool, *AcmeCertificate) { - domain := domains[0] - if !c.haveCertificate(domain) { +// GetStoredCertificate returns the locally stored certificate for the given domains +func (c *Client) GetStoredCertificate(certName string, domains []string) (bool, *AcmeCertificate) { + logrus.Debugf("Looking up stored certificate by name '%s'", certName) + if !c.haveCertificateByName(certName) { return false, nil } - acmeCert, err := c.loadCertificate(domain) + acmeCert, err := c.loadCertificateByName(certName) if err != nil { - // Don't fail here. Try to create a new certificate instead. - logrus.Error(err.Error()) + // Don't quit. Try to issue a new certificate instead. + logrus.Errorf("Error loading certificate '%s': %v", certName, err) return false, nil } + + // check if the DNS names are a match + if dnsNames := dnsNamesIdentifier(domains); acmeCert.DnsNames != dnsNames { + logrus.Infof("Stored certificate does not have matching domain names: '%s' ", acmeCert.DnsNames) + return false, nil + } + return true, &acmeCert } -func (c *Client) haveCertificate(domain string) bool { - certPath := c.CertPath(domain) +func (c *Client) haveCertificateByName(certName string) bool { + certPath := c.CertPath(certName) if _, err := os.Stat(path.Join(certPath, "metadata.json")); err != nil { - logrus.Debugf("No existing acme certificate resource found for '%s'", domain) + logrus.Debugf("No certificate in path '%s'", certPath) return false } + return true } -func (c *Client) loadCertificate(domain string) (AcmeCertificate, error) { +func (c *Client) loadCertificateByName(certName string) (AcmeCertificate, error) { var acmeCert AcmeCertificate - certPath := c.CertPath(domain) + certPath := c.CertPath(certName) - logrus.Debugf("Loading acme certificate resource for '%s' from '%s'", domain, certPath) + logrus.Debugf("Loading certificate '%s' from '%s'", certName, certPath) certIn := path.Join(certPath, "fullchain.pem") privIn := path.Join(certPath, "privkey.pem") @@ -226,76 +226,113 @@ func (c *Client) loadCertificate(domain string) (AcmeCertificate, error) { certBytes, err := ioutil.ReadFile(certIn) if err != nil { - return acmeCert, fmt.Errorf("Failed to load certificate for domain '%s': %s", domain, err.Error()) + return acmeCert, fmt.Errorf("Failed to load certificate from '%s': %v", certIn, err) } metaBytes, err := ioutil.ReadFile(metaIn) if err != nil { - return acmeCert, fmt.Errorf("Failed to load meta data for domain '%s': %s", domain, err.Error()) + return acmeCert, fmt.Errorf("Failed to load meta data from '%s': %v", metaIn, err) } keyBytes, err := ioutil.ReadFile(privIn) if err != nil { - return acmeCert, fmt.Errorf("Failed to load private key for domain '%s': %s", domain, err.Error()) + return acmeCert, fmt.Errorf("Failed to load private key from '%s': %v", privIn, err) } err = json.Unmarshal(metaBytes, &acmeCert) if err != nil { - return acmeCert, fmt.Errorf("Failed to unmarshal meta data for domain '%s': %s", domain, err.Error()) + return acmeCert, fmt.Errorf("Failed to unmarshal json meta data from '%s': %v", metaIn, err) } acmeCert.PrivateKey = keyBytes acmeCert.Certificate = certBytes - return acmeCert, nil } -func (c *Client) saveCertificate(acmeCert AcmeCertificate) { - certPath := c.CertPath(acmeCert.Domain) +func (c *Client) saveCertificate(certName, dnsNames string, certRes lego.CertificateResource) (*AcmeCertificate, error) { + expiryDate, err := lego.GetPEMCertExpiration(certRes.Certificate) + if err != nil { + return nil, fmt.Errorf("Failed to read certificate expiry date: %v", err) + } + serialNumber, err := getPEMCertSerialNumber(certRes.Certificate) + if err != nil { + return nil, fmt.Errorf("Failed to read certificate serial number: %v", err) + } + + acmeCert := AcmeCertificate{ + CertificateResource: certRes, + ExpiryDate: expiryDate, + SerialNumber: serialNumber, + DnsNames: dnsNames, + } + + certPath := c.CertPath(certName) maybeCreatePath(certPath) - logrus.Debugf("Saving acme certificate resource for '%s' to '%s'", acmeCert.Domain, certPath) + logrus.Debugf("Saving certificate '%s' to path '%s'", certName, certPath) certOut := path.Join(certPath, "fullchain.pem") privOut := path.Join(certPath, "privkey.pem") metaOut := path.Join(certPath, "metadata.json") - err := ioutil.WriteFile(certOut, acmeCert.Certificate, 0600) + err = ioutil.WriteFile(certOut, acmeCert.Certificate, 0600) if err != nil { - logrus.Fatalf("Failed to save certificate for domain %s\n\t%s", acmeCert.Domain, err.Error()) + return nil, fmt.Errorf("Failed to save certificate to '%s': %v", certOut, err) } + logrus.Infof("Certificate saved to '%s'", certOut) + err = ioutil.WriteFile(privOut, acmeCert.PrivateKey, 0600) if err != nil { - logrus.Fatalf("Failed to save private key for domain %s\n\t%s", acmeCert.Domain, err.Error()) + return nil, fmt.Errorf("Failed to save private key to '%s': %v", privOut, err) } + logrus.Infof("Private key saved to '%s'", privOut) + jsonBytes, err := json.MarshalIndent(acmeCert, "", "\t") if err != nil { - logrus.Fatalf("Failed to marshal meta data for domain %s\n\t%s", acmeCert.Domain, err.Error()) + return nil, fmt.Errorf("Failed to marshal meta data for certificate '%s': %v", certName, err) } err = ioutil.WriteFile(metaOut, jsonBytes, 0600) if err != nil { - logrus.Fatalf("Failed to save meta data for domain %s\n\t%s", acmeCert.Domain, err.Error()) + return nil, fmt.Errorf("Failed to save meta data to '%s': %v", metaOut, err) } + + return &acmeCert, nil } func (c *Client) ConfigPath() string { - path := path.Join(ConfigDir, strings.ToLower(string(c.apiVersion))) + path := path.Join(StorageDir, strings.ToLower(string(c.apiVersion))) maybeCreatePath(path) return path } -func (c *Client) CertPath(domain string) string { - return path.Join(c.ConfigPath(), "certificates", domain) +func (c *Client) CertPath(certName string) string { + return path.Join(c.ConfigPath(), "certs", safeFileName(certName)) +} + +func dnsNamesIdentifier(domains []string) string { + return strings.Join(domains, "|") } func maybeCreatePath(path string) { if _, err := os.Stat(path); os.IsNotExist(err) { err = os.MkdirAll(path, 0700) if err != nil { - logrus.Fatalf("Error creating path: %v", err) + logrus.Fatalf("Failed to create path: %v", err) } } } + +// safeFileName replaces separators with dashes and removes all +// characters other than alphanumerics, dashes, underscores and dots. +func safeFileName(str string) string { + separators := regexp.MustCompile(`[ /&=+:]`) + illegals := regexp.MustCompile(`[^[:alnum:]-_.]`) + dashes := regexp.MustCompile(`[\-]+`) + str = separators.ReplaceAllString(str, "-") + str = illegals.ReplaceAllString(str, "") + str = dashes.ReplaceAllString(str, "-") + return str +} diff --git a/letsencrypt/crypto.go b/letsencrypt/crypto.go index 2b89ab1..0574759 100644 --- a/letsencrypt/crypto.go +++ b/letsencrypt/crypto.go @@ -77,7 +77,7 @@ func loadPrivateKey(file string) (crypto.PrivateKey, error) { return nil, fmt.Errorf("Unknown private key type.") } -func getPEMCertSerialNo(cert []byte) (string, error) { +func getPEMCertSerialNumber(cert []byte) (string, error) { pemBlock, _ := pem.Decode(cert) if pemBlock == nil { return "", fmt.Errorf("Pem decode did not yield a valid block") diff --git a/main.go b/main.go index 46b3588..d375182 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "flag" "os" "github.com/Sirupsen/logrus" @@ -10,14 +11,18 @@ var ( // Must be set at build Version string Git string + + debug bool ) func init() { + flag.BoolVar(&debug, "debug", false, "Enable debugging") logrus.SetFormatter(&logrus.TextFormatter{DisableTimestamp: true}) logrus.SetOutput(os.Stdout) } func main() { + flag.Parse() logrus.Infof("Starting Let's Encrypt Certificate Manager %s %s", Version, Git) context := &Context{} context.InitContext() diff --git a/manager.go b/manager.go index 7b592ca..4e1d963 100644 --- a/manager.go +++ b/manager.go @@ -21,44 +21,44 @@ func (c *Context) Run() { } func (c *Context) startup() { - var haveLocal, haveRemote bool - ok, acmeCert := c.Acme.GetStoredCertificate(c.Domains) + var storedLocally, storedInRancher bool + ok, acmeCert := c.Acme.GetStoredCertificate(c.CertificateName, c.Domains) if ok { - haveLocal = true + storedLocally = true c.ExpiryDate = acmeCert.ExpiryDate - logrus.Infof("Found locally stored certificate for '%s'", strings.Join(c.Domains, " | ")) + logrus.Infof("Found locally stored certificate '%s'", c.CertificateName) } - rancherCert, err := c.Rancher.FindCertByName(c.RancherCertName) + rancherCert, err := c.Rancher.FindCertByName(c.CertificateName) if err != nil { - logrus.Fatalf("Error looking up Rancher certificates: %v", err) + logrus.Fatalf("Could not lookup certificate in Rancher API: %v", err) } if rancherCert != nil { - haveRemote = true + storedInRancher = true c.RancherCertId = rancherCert.Id - logrus.Infof("Found existing Rancher certificate '%s'", rancherCert.Name) + logrus.Infof("Found existing certificate '%s' in Rancher", c.CertificateName) } - if haveLocal && haveRemote { + if storedLocally && storedInRancher { if rancherCert.SerialNumber == acmeCert.SerialNumber { - logrus.Infof("Managing renewal of Rancher certificate '%s'", rancherCert.Name) + logrus.Infof("Managing renewal of certificate '%s'", c.CertificateName) return } - logrus.Infof("Certificate serial number mismatch. Overwriting Rancher certificate '%s'", rancherCert.Name) + logrus.Infof("Serial number mismatch between Rancher and local certificate '%s'", c.CertificateName) c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate) return } - if haveLocal && !haveRemote { - logrus.Infof("Adding Rancher certificate '%s'", rancherCert.Name) + if storedLocally && !storedInRancher { + logrus.Debugf("Adding certificate '%s' to Rancher", c.CertificateName) c.addRancherCert(acmeCert.PrivateKey, acmeCert.Certificate) return } - logrus.Infof("Trying to obtain SSL certificate for %s", strings.Join(c.Domains, " | ")) + logrus.Infof("Trying to obtain SSL certificate (%s) from Let's Encrypt CA", strings.Join(c.Domains, ",")) - acmeCert, failures := c.Acme.Issue(c.Domains) + acmeCert, failures := c.Acme.Issue(c.CertificateName, c.Domains) if len(failures) > 0 { for k, v := range failures { logrus.Errorf("[%s] Error obtaining certificate: %s", k, v.Error()) @@ -66,12 +66,12 @@ func (c *Context) startup() { os.Exit(1) } - logrus.Infof("Successfully obtained SSL certificate") + logrus.Infof("Certificate obtained successfully") c.ExpiryDate = acmeCert.ExpiryDate - if haveRemote { - logrus.Infof("Overwriting Rancher certificate '%s'", rancherCert.Name) + if storedInRancher { + logrus.Debugf("Overwriting Rancher certificate '%s'", c.CertificateName) c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate) return } @@ -80,36 +80,36 @@ func (c *Context) startup() { } func (c *Context) addRancherCert(privateKey, cert []byte) { - rancherCert, err := c.Rancher.AddCertificate(c.RancherCertName, CERT_DESCRIPTION, privateKey, cert) + rancherCert, err := c.Rancher.AddCertificate(c.CertificateName, CERT_DESCRIPTION, privateKey, cert) if err != nil { - logrus.Fatalf("Failed to add Rancher certificate '%s': %v", c.RancherCertName, err) + logrus.Fatalf("Failed to add Rancher certificate '%s': %v", c.CertificateName, err) } c.RancherCertId = rancherCert.Id - logrus.Infof("Added Rancher certificate '%s'", c.RancherCertName) + logrus.Infof("Certificate '%s' added to Rancher", c.CertificateName) } func (c *Context) updateRancherCert(privateKey, cert []byte) { err := c.Rancher.UpdateCertificate(c.RancherCertId, CERT_DESCRIPTION, privateKey, cert) if err != nil { - logrus.Fatalf("Failed to update Rancher certificate '%s': %v", c.RancherCertName, err) + logrus.Fatalf("Failed to update Rancher certificate '%s': %v", c.CertificateName, err) } - logrus.Infof("Updated Rancher certificate '%s'", c.RancherCertName) + logrus.Infof("Updated Rancher certificate '%s'", c.CertificateName) err = c.Rancher.UpgradeLoadBalancers(c.RancherCertId) if err != nil { - logrus.Fatalf("Error upgrading load balancers: %v", err) + logrus.Fatalf("Failed to upgrade load balancers: %v", err) } } func (c *Context) renew() { - logrus.Infof("Trying to renew certificate for '%s'", strings.Join(c.Domains, " | ")) + logrus.Infof("Trying to obtain renewed SSL certificate (%s) from Let's Encrypt CA", strings.Join(c.Domains, ",")) - acmeCert, err := c.Acme.Renew(c.Domains) + acmeCert, err := c.Acme.Renew(c.CertificateName) if err != nil { logrus.Fatalf("Failed to renew certificate: %v", err) } - logrus.Infof("Successfully renewed certificate") + logrus.Infof("Certificate renewed successfully") c.ExpiryDate = acmeCert.ExpiryDate c.updateRancherCert(acmeCert.PrivateKey, acmeCert.Certificate) @@ -123,11 +123,11 @@ func (c *Context) timer() <-chan time.Time { left = 10 * time.Second } - logrus.Infof("Next certificate renewal scheduled for %s", next.Format("2006/01/02 15:04 MST")) + logrus.Infof("Certificate renewal scheduled for %s", next.Format("2006/01/02 15:04 MST")) - // Debug option set to true enables test mode + // Debug option forces renewal if c.Debug { - logrus.Debug("Test mode enabled: certificate will be renewed in 120 seconds") + logrus.Debug("Debug mode: Forced certificate renewal in 120 seconds") left = 120 * time.Second }